package com.xd.core.business.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.xd.common.base.CustomRowHeightColWidthHandler;
import com.xd.common.base.RowHeightColWidthModel;
import com.xd.common.constant.SysConstant;
import com.xd.common.exception.XkdException;
import com.xd.common.util.AESUtil;
import com.xd.common.util.OrderNumUtil;
import com.xd.common.util.SecurityUtil;
import com.xd.common.util.XkdUtil;
import com.xd.core.api.dto.OutAmountDto;
import com.xd.core.api.entity.CardProtocolInfo;
import com.xd.core.api.entity.ChannelPayTow;
import com.xd.core.api.mapper.CardProtocolInfoMapper;
import com.xd.core.api.mapper.ChannelPayTowMapper;
import com.xd.core.api.service.ApiContractService;
import com.xd.core.api.service.ApiService;
import com.xd.core.api.service.DLApiService;
import com.xd.core.api.service.impl.DLApiServiceImpl;
import com.xd.core.business.dto.AccountDto;
import com.xd.core.business.dto.OutInfoAppQuery;
import com.xd.core.business.dto.OutInfoDto;
import com.xd.core.business.dto.OutInfoQuery;
import com.xd.core.business.entity.*;
import com.xd.core.business.mapper.*;
import com.xd.core.business.service.ICreditService;
import com.xd.core.business.service.IOutInfoService;
import com.xd.core.business.vo.*;
import com.xd.core.company.dto.HomeQueryDto;
import com.xd.core.company.entity.Company;
import com.xd.core.company.entity.CompanyAccount;
import com.xd.core.company.mapper.CompanyAccountMapper;
import com.xd.core.company.mapper.CompanyMapper;
import com.xd.core.company.service.ICompanyService;
import com.xd.core.company.vo.OutCounts;
import com.xd.core.debt.entity.Debt;
import com.xd.core.debt.mapper.DebtMapper;
import com.xd.core.finance.dto.LendingRecordQueryDto;
import com.xd.core.finance.vo.LendingRecordQueryVo;
import com.xd.core.mission.entity.MissionCenterInfo;
import com.xd.core.mission.mapper.MissionCenterInfoMapper;
import com.xd.core.note.IAsyncService;
import com.xd.core.openapi.vo.IncomingIdVo;
import com.xd.core.openapi.vo.OutDetailVo;
import com.xd.core.product.entity.Product;
import com.xd.core.product.entity.ProductCompany;
import com.xd.core.product.mapper.ProductCompanyMapper;
import com.xd.core.product.mapper.ProductMapper;
import com.xd.core.system.entity.SysUser;
import com.xd.core.system.mapper.SysUserMapper;
import com.xd.core.system.service.ISysUserService;
import com.xd.core.system.service.MsmService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 放款表 服务实现类
 * </p>
 *
 * @author ZX
 * @since 2025-02-27
 */
@Slf4j
@Service
public class OutInfoServiceImpl extends ServiceImpl<OutInfoMapper, OutInfo> implements IOutInfoService {

    @Value("${image.localPathDir}")
    private String localPathDir;
    @Value("${image.exportFile}")
    private String exportFile;
    @Value("${aes.key}")
    private String aesKey;
    @Value("${baofush.loans_out_no}")
    private String loans_out_no;
    @Value("${spring.profiles.active}")
    private String active;
    @Value("${baofush.loans_out_account_offline}")
    private String passOneAccount;
    @Value("${baofush.tow_loans_out_account_offline}")
    private String passTwoAccount;


    @Autowired
    private MissionCenterInfoMapper missionCenterInfoMapper;
    @Autowired
    private IAsyncService iAsyncService;
    @Autowired
    private OutInfoMapper outInfoMapper;
    @Autowired
    private ISysUserService iSysUserService;
    @Autowired
    private IncomingInfoMapper incomingInfoMapper;
    @Autowired
    private CreditMapper creditMapper;
    @Autowired
    private DebtMapper debtMapper;
    @Autowired
    private RepaymentScheduleMapper repaymentScheduleMapper;
    @Autowired
    private DLApiService dlApiService;
    @Autowired
    private ApiService apiService;
    @Autowired
    private OutOrderInfoMapper outOrderInfoMapper;
    @Autowired
    private ChannelPayTowMapper channelPayTowMapper;
    @Autowired
    private ContractMapper contractMapper;
    @Autowired
    private CompanyMapper companyMapper;
    @Autowired
    private ICompanyService iCompanyService;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private ProductCompanyMapper productCompanyMapper;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private CompanyAccountMapper companyAccountMapper;
    @Autowired
    private IncomingCommonLoanMapper incomingCommonLoanMapper;
    @Autowired
    private ICreditService iCreditService;
    @Autowired
    private ApiContractService apiContractService;
    @Autowired
    private CardProtocolInfoMapper cardProtocolInfoMapper;
    @Autowired
    private MsmService msmService;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    private final static String transferOrderLock = "TransferOrder";
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private CarCreditMapper carCreditMapper;
    @Autowired
    private GuaranteeSlipMapper guaranteeSlipMapper;
    @Autowired
    private DLApiServiceImpl dlApiServiceimpl;


    @Override
    public Page<OutInfoPageVo> getList(OutInfoQuery query) {
        String companyType = SecurityUtil.getLoginUser().getUser().getCompanyType();
        Integer companyId = query.getCompanyId();
        List<Integer> userIds = null;
        if (!companyType.equals(SysConstant.USER_COMPANY_TYPE_ONE) && !companyType.equals(SysConstant.USER_COMPANY_TYPE_FOUR)) {//不是平台和集团
            companyId = SecurityUtil.getLoginCompanyId();
            userIds = iSysUserService.getUserIdsByGroup();
        }
        PageHelper.startPage(query.getPageNum(), query.getPageSize());
        return (Page<OutInfoPageVo>) outInfoMapper.getList(query, userIds, companyId, companyType);
    }

    @Override
    public OutInfoVo getOutDetail(Integer id) {
        OutInfoVo detail = outInfoMapper.getDetail(id);
        List<IncomingCommonLoan> incomingCommonLoans = incomingCommonLoanMapper.selectResult(id);
        detail.setIncomingCommonLoans(incomingCommonLoans);
        Integer i = outInfoMapper.selectCount(new LambdaQueryWrapper<OutInfo>().eq(OutInfo::getIncomingId, id).ne(OutInfo::getOutStatus, "0"));
        Integer status = detail.getStatus();
        String flag = "0";
        if (i > 0 || (status < Integer.parseInt(SysConstant.BUSINESS_STATUS_FIVE) || status > Integer.parseInt(SysConstant.BUSINESS_STATUS_ELEVEN))) {
            flag = "1";
        }
        detail.setFlag(flag);
        return detail;
    }


    @Transactional(rollbackFor = Exception.class)
    public void testAddOutInfo(OutInfoDto outInfoDto) throws Exception {
        Integer incomingId = outInfoDto.getIncomingId();
        Integer outId = outInfoDto.getOutId();
        OutInfo outInfo = outInfoMapper.selectById(outId);
        IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
        if (SysConstant.ORDER_DISABLE.equals(incomingInfo.getIsEnable())) {
            throw new XkdException("该订单已被平台停用");
        }
        Integer companyId = outInfoDto.getCompanyId();
        CompanyAccount companyAccount = companyAccountMapper.selectById(companyId);
        if (ObjectUtil.isNotEmpty(companyAccount) && companyAccount.getPrestoreMemberId().isEmpty()) {
            throw new XkdException("请绑定线下分账商户号");
        }

        //校验验证码
//        String phone = checkOutCode(outInfoDto, companyId);

        RLock lock = redissonClient.getLock(transferOrderLock + companyId);
        String lockName = SysConstant.FEE_LOCK + companyId;
        RLock lock2 = redissonClient.getLock(lockName);

        try {
            log.info("尝试获取锁: {}", lockName);
            lock2.lock();
            log.info("成功获取锁: {}", lockName);
            Company company = companyMapper.selectById(companyId);
            //判断服务费是为负
            String accountingType = company.getAccountingType();
            BigDecimal balance = BigDecimal.ZERO;
            if (!SysConstant.ACCOUNTING_TYPE_ONE.equals(accountingType)) {
                //集团服务费余额
                balance = companyMapper.selectOne(new LambdaQueryWrapper<Company>().eq(Company::getId, company.getBlocId())).getBalance();
            } else {
                //集团服务费余额
                balance = companyMapper.selectBalance(companyId);
            }
            if (balance.compareTo(BigDecimal.ZERO) < 0)
                throw new XkdException("服务费不足，无法放款");
        } catch (Exception e) {
            throw new XkdException(e.getMessage());
        } finally {
            if (lock2.isHeldByCurrentThread()) {
                lock2.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), lockName);
            }
        }
        try {
            log.info("尝试获取锁: {}", transferOrderLock + companyId);
            lock.lock();
            log.info("成功获取锁: {}", transferOrderLock + companyId);
            //查询是否绑卡
            String cardStatus = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, outInfoDto.getIncomingId())).getCardStatus();
            if ("0".equals(cardStatus)) {
                throw new XkdException("还款人未绑定银行卡");
            }

            Integer count = contractMapper.selectCount(new LambdaQueryWrapper<Contract>()
                    .eq(Contract::getOutId, outId).in(Contract::getType, SysConstant.CONTRACT_TYPE_SIX, SysConstant.CONTRACT_TYPE_SEVEN)
                    .eq(Contract::getStatus, SysConstant.CONTRACT_DO));
            if (count != 2) {
                throw new XkdException("用户有合同未签署完毕");
            }
            if (SysConstant.BUSINESS_STATUS_THIRTEEN.equals(outInfo.getStatus()) || SysConstant.BUSINESS_STATUS_TWELVE.equals(outInfo.getStatus())) {
                throw new XkdException("业务状态异常");
            }

            BigDecimal loanAmount = outInfoDto.getLoanAmount();
            BigDecimal outLoan = outInfo.getOutLoan();
            //判断是否超放
            if (loanAmount.compareTo(outLoan) > 0) {
                throw new XkdException("放款金额大于申请提款金额");
            }
            BeanUtil.copyProperties(outInfoDto, outInfo);
            outInfo.setOutStatus(SysConstant.OUT_INFO_PAY_TOW);
            outInfo.setStatus(SysConstant.BUSINESS_STATUS_TWELVE);
            updateById(outInfo);
            Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId));
            //插入订单表
            String order = OrderNumUtil.getOutAmountOrder();
            long i = loanAmount.multiply(new BigDecimal(100)).intValue();
            if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {//通道一
                OutOrderInfo outOrderInfo = new OutOrderInfo();
                outOrderInfo.setOrderId(outInfo.getOutId());
                outOrderInfo.setOrderNo(order);
                outOrderInfo.setAmount(i);
                outOrderInfo.setCompanyId(companyId);
                outOrderInfo.setIncomingId(incomingId);
                outOrderInfo.setOutId(outId);
                outOrderInfo.setToAccName(credit.getAccountName());
                outOrderInfo.setOutTime(new Date());
                outOrderInfo.setToAccNo(credit.getReceivingAccount());
                outOrderInfo.setToBankName(credit.getBank());
                outOrderInfo.setTransCardId(outInfoDto.getTransCardId());
                outOrderInfo.setTransCnap(outInfoDto.getToBankCode());
                String client_trans_id = "T" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
                outOrderInfo.setClientTransId(client_trans_id);
                outOrderInfoMapper.insert(outOrderInfo);
                //调拨提现---》成功----》放款
                TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                    @Override
                    public void afterCommit() {//事务成功后执行
                        try {
                            testOutAmount(incomingId, outInfo.getOutId(), companyId, i, order);
                        } catch (Exception e) {
                            log.error("testAddOutInfo 异常", e); // 打印堆栈信息
                            throw new XkdException(e.getMessage());
                        }
                    }
                });
            } else if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {//通道二
                ChannelPayTow channelPayTow = new ChannelPayTow();
                channelPayTow.setOrderNo(order);
                channelPayTow.setPayType("1");
                channelPayTow.setAmount(i);
                channelPayTow.setAccountType("1");
                channelPayTow.setPayStatus("1");
                channelPayTow.setIncomingId(incomingId);
                channelPayTow.setOutId(outId);
                channelPayTow.setCompanyId(companyId);
                channelPayTow.setCreateTime(new Date());
                channelPayTow.setToAccName(credit.getAccountName());
                channelPayTow.setToAccNo(credit.getReceivingAccount());
                channelPayTow.setToBankName(credit.getBank());
                channelPayTow.setTransCardId(outInfoDto.getTransCardId());
                channelPayTowMapper.insert(channelPayTow);

                OutAmountDto outAmountDto = new OutAmountDto();
                outAmountDto.setCompanyId(credit.getCompanyId());
                outAmountDto.setToBankName(credit.getBank());
                outAmountDto.setToAccName(credit.getAccountName());
                outAmountDto.setToAccNo(credit.getReceivingAccount());
                outAmountDto.setId(outInfo.getOutId());
                outAmountDto.setOutId(outId);
                outAmountDto.setTransCardId(outInfoDto.getTransCardId());
                outAmountDto.setIncomingId(incomingId);
                outAmountDto.setTransMoney(loanAmount + "");
                outAmountDto.setChannelType(outInfoDto.getPayPass());
                outAmountDto.setTransCnap(outInfoDto.getToBankCode());
                outAmountDto.setOrder(order);
                // 确保锁的释放在事务提交之后
                TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                    @Override
                    public void afterCommit() {//事务成功后执行
                        try {
                            testPassTowOutAmount(incomingId, outInfo.getOutId(), companyId, i, order);
                        } catch (Exception e) {
                            log.error("testOutAmount 异常", e);
                            throw new XkdException("放款失败");
                        }
                    }
                });
            }
        } catch (Exception e) {
            throw new XkdException(e.getMessage());
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), transferOrderLock + companyId);
            }
            //放款后删除验证码
//            redisTemplate.delete("out_amount:" + outInfoDto.getOutTimestamp() + phone);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void testAddOutInfobaodan(OutInfoDto outInfoDto) throws Exception {
        Integer incomingId = outInfoDto.getIncomingId();
        Integer outId = outInfoDto.getOutId();
        OutInfo outInfo = outInfoMapper.selectById(outId);
        IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
        if (SysConstant.ORDER_DISABLE.equals(incomingInfo.getIsEnable())) {
            throw new XkdException("该订单已被平台停用");
        }
        Integer companyId = outInfoDto.getCompanyId();
        CompanyAccount companyAccount = companyAccountMapper.selectById(companyId);
        if (ObjectUtil.isNotEmpty(companyAccount) && companyAccount.getPrestoreMemberId().isEmpty()) {
            throw new XkdException("请绑定线下分账商户号");
        }
        //校验合同是否签署完毕
        Integer count = contractMapper.selectCount(new LambdaQueryWrapper<Contract>()
                .eq(Contract::getOutId, outInfo.getOutId())
                .eq(Contract::getType, SysConstant.CONTRACT_TYPE_TWELVE)
                .eq(Contract::getStatus, SysConstant.CONTRACT_DO));
        if (count != 1) {
            throw new XkdException("用户合同未签署完毕");
        }
        //校验验证码
//        String phone = checkOutCode(outInfoDto, companyId);

        RLock lock = redissonClient.getLock(transferOrderLock + companyId);
        String lockName = SysConstant.FEE_LOCK + companyId;
        RLock lock2 = redissonClient.getLock(lockName);
        Company company = null;
        try {
            log.info("尝试获取锁: {}", lockName);
            lock2.lock();
            log.info("成功获取锁: {}", lockName);
            company = companyMapper.selectById(companyId);
            List<String> collect = Arrays.stream(company.getThoroughFare().split(",")).map(String::valueOf).collect(Collectors.toList());
            if (!collect.contains(outInfoDto.getPayPass())) {
                throw new XkdException("通道类型错误");
            }
            //判断服务费是为负
            String accountingType = company.getAccountingType();
            //判断
            BigDecimal balance = BigDecimal.ZERO;
            if (!SysConstant.ACCOUNTING_TYPE_ONE.equals(accountingType)) {
                //集团服务费余额
                balance = companyMapper.selectOne(new LambdaQueryWrapper<Company>().eq(Company::getId, company.getBlocId())).getBalance();
            } else {
                //资方服务费余额
                balance = company.getBalance();
            }
            if (balance.compareTo(BigDecimal.ZERO) < 0)
                throw new XkdException("服务费不足，无法放款");
        } catch (Exception e) {
            throw new XkdException(e.getMessage());
        } finally {
            if (lock2.isHeldByCurrentThread()) {
                lock2.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), lockName);
            }
        }
        try {
            log.info("尝试获取锁: {}", transferOrderLock + companyId);
            lock.lock();
            log.info("成功获取锁: {}", transferOrderLock + companyId);
            //查询是否绑卡
            String cardStatus = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, outInfoDto.getIncomingId())).getCardStatus();
            if ("0".equals(cardStatus)) {
                throw new XkdException("还款人未绑定银行卡");
            }
            if (SysConstant.BUSINESS_STATUS_THIRTEEN.equals(outInfo.getStatus()) || SysConstant.BUSINESS_STATUS_TWELVE.equals(outInfo.getStatus())) {
                throw new XkdException("业务状态异常");
            }

            BigDecimal loanAmount = outInfoDto.getLoanAmount();
            BeanUtil.copyProperties(outInfoDto, outInfo);
            outInfo.setOutLoan(loanAmount);
            outInfo.setOutStatus(SysConstant.OUT_INFO_PAY_TOW);
            incomingInfoMapper.updateStatus(SysConstant.BUSINESS_STATUS_TWELVE, incomingId);
            updateById(outInfo);
            Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId));
            //插入订单表
            String order = OrderNumUtil.getOutAmountOrder();
            long i = loanAmount.multiply(new BigDecimal(100)).intValue();
            if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {//通道一
                OutOrderInfo outOrderInfo = new OutOrderInfo();
                outOrderInfo.setOrderId(outInfo.getOutId());
                outOrderInfo.setOrderNo(order);
                outOrderInfo.setAmount(i);
                outOrderInfo.setCompanyId(companyId);
                outOrderInfo.setIncomingId(incomingId);
                outOrderInfo.setOutId(outId);
                outOrderInfo.setToAccName(outInfoDto.getToAccName());
                outOrderInfo.setOutTime(new Date());
                outOrderInfo.setToAccNo(outInfoDto.getToAccNo());
                outOrderInfo.setTransCnap(outInfoDto.getToBankCode());
                outOrderInfo.setToBankName(outInfoDto.getBank());
                outOrderInfo.setTransCardId(outInfoDto.getTransCardId());
                String client_trans_id = "T" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
                outOrderInfo.setClientTransId(client_trans_id);
                outOrderInfoMapper.insert(outOrderInfo);
                //调拨提现---》成功----》放款
                TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                    @Override
                    public void afterCommit() {//事务成功后执行
                        try {
                            testOutAmount(incomingId, outInfo.getOutId(), companyId, i, order);
                        } catch (Exception e) {
                            log.error("testOutAmount 异常", e); // 打印堆栈信息
                            throw new XkdException(e.getMessage());
                        }
                    }
                });
            } else if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {//通道二
                ChannelPayTow channelPayTow = new ChannelPayTow();
                channelPayTow.setOrderNo(order);
                channelPayTow.setPayType("1");
                channelPayTow.setAmount(i);
                channelPayTow.setAccountType("1");
                channelPayTow.setPayStatus("1");
                channelPayTow.setIncomingId(incomingId);
                channelPayTow.setOutId(outId);
                channelPayTow.setCompanyId(companyId);
                channelPayTow.setCreateTime(new Date());
                channelPayTow.setToAccName(outInfoDto.getToAccName());
                channelPayTow.setToAccNo(outInfoDto.getToAccNo());
                channelPayTow.setToBankName(outInfoDto.getBank());
                channelPayTow.setTransCardId(outInfoDto.getTransCardId());
                channelPayTowMapper.insert(channelPayTow);

                OutAmountDto outAmountDto = new OutAmountDto();
                outAmountDto.setCompanyId(credit.getCompanyId());
                outAmountDto.setToBankName(outInfoDto.getBank());
                outAmountDto.setToAccName(outInfoDto.getToAccName());
                outAmountDto.setToAccNo(outInfoDto.getToAccNo());
                outAmountDto.setId(outInfo.getOutId());
                outAmountDto.setOutId(outId);
                outAmountDto.setTransCnap(outInfoDto.getToBankCode());
                outAmountDto.setTransCardId(outInfoDto.getTransCardId());
                outAmountDto.setIncomingId(incomingId);
                outAmountDto.setTransMoney(loanAmount + "");
                outAmountDto.setChannelType(outInfoDto.getPayPass());
                outAmountDto.setOrder(order);
                // 确保锁的释放在事务提交之后
                TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                    @Override
                    public void afterCommit() {//事务成功后执行
                        try {
                            testPassTowOutAmount(incomingId, outInfo.getOutId(), companyId, i, order);
                        } catch (Exception e) {
                            log.error("testAddOutInfo 异常", e); // 打印堆栈信息
                            throw new XkdException("放款失败");
                        }
                    }
                });
            }
        } catch (Exception e) {
            throw new XkdException(e.getMessage());
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), transferOrderLock + companyId);
            }
            //放款后删除验证码
//            redisTemplate.delete("out_amount:" + outInfoDto.getOutTimestamp() + phone);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addOutInfo(OutInfoDto outInfoDto) throws Exception {
        //判断资方账户是否被冻结
        Integer companyId = outInfoDto.getCompanyId();
        Company company = companyMapper.selectById(companyId);
        if ("1".equals(company.getIsFreeze())) {
            throw new XkdException("账户已被冻结");
        }
        if (!active.equals("prd")) {
            testAddOutInfo(outInfoDto);
            return;
        }
        Integer incomingId = outInfoDto.getIncomingId();
        Integer outId = outInfoDto.getOutId();
        OutInfo outInfo = outInfoMapper.selectById(outId);
        IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
        if (SysConstant.ORDER_DISABLE.equals(incomingInfo.getIsEnable())) {
            throw new XkdException("该订单已被平台停用");
        }
        CompanyAccount companyAccount = companyAccountMapper.selectById(companyId);
        if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {//通道一验证
            if (ObjectUtil.isNotEmpty(companyAccount) && companyAccount.getPrestoreMemberId().isEmpty()) {
                throw new XkdException("请绑定线下分账商户号");
            }
        }
        //校验验证码
//        String phone = checkOutCode(outInfoDto, companyId);

        RLock lock = redissonClient.getLock(transferOrderLock + companyId);
        String lockName = SysConstant.FEE_LOCK + companyId;
        RLock lock2 = redissonClient.getLock(lockName);
        try {
            log.info("尝试获取锁: {}", lockName);
            lock2.lock();
            log.info("成功获取锁: {}", lockName);
            company = companyMapper.selectById(companyId);
            List<String> collect = Arrays.stream(company.getThoroughFare().split(",")).map(String::valueOf).collect(Collectors.toList());
            if (!collect.contains(outInfoDto.getPayPass())) {
                throw new XkdException("通道类型错误");
            }
            //判断服务费是为负
            String accountingType = company.getAccountingType();
            //判断
            BigDecimal balance = BigDecimal.ZERO;
            if (!SysConstant.ACCOUNTING_TYPE_ONE.equals(accountingType)) {
                //集团服务费余额
                balance = companyMapper.selectOne(new LambdaQueryWrapper<Company>().eq(Company::getId, company.getBlocId())).getBalance();
            } else {
                //资方服务费余额
                balance = company.getBalance();
            }
            if (balance.compareTo(BigDecimal.ZERO) < 0)
                throw new XkdException("服务费不足，无法放款");
        } catch (Exception e) {
            throw new XkdException(e.getMessage());
        } finally {
            if (lock2.isHeldByCurrentThread()) {
                lock2.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), lockName);
            }
        }
        try {
            log.info("尝试获取锁: {}", transferOrderLock + companyId);
            lock.lock();
            log.info("成功获取锁: {}", transferOrderLock + companyId);
            //查询是否绑卡
            String cardStatus = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, outInfoDto.getIncomingId())).getCardStatus();
            if ("0".equals(cardStatus)) {
                throw new XkdException("还款人未绑定银行卡");
            }

            Integer count = contractMapper.selectCount(new LambdaQueryWrapper<Contract>()
                    .eq(Contract::getOutId, outId)
                    .eq(Contract::getType, SysConstant.CONTRACT_TYPE_TOW)
                    .eq(Contract::getStatus, SysConstant.CONTRACT_TODO));
            if (count != 0)
                throw new XkdException("用户合同未签署完毕");

            if (SysConstant.BUSINESS_STATUS_THIRTEEN.equals(outInfo.getStatus()) || SysConstant.BUSINESS_STATUS_TWELVE.equals(outInfo.getStatus())) {
                throw new XkdException("业务状态异常");
            }
            //查询放款账户余额是否充足
            long oa = 0;//剩余余额
            if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
                oa = company.getOutBalance() - company.getOutBalanceFreeze();
            } else if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {
                oa = company.getTowBaofuBalance() - company.getTowOutBalanceFreeze();
            }
            if (outInfoDto.getLoanAmount().compareTo(new BigDecimal(oa).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)) > 0)
                throw new XkdException("放款账户余额不足");

            BigDecimal loanAmount = outInfoDto.getLoanAmount();
            BigDecimal outLoan = outInfo.getOutLoan();
            //判断是否超放
            if (loanAmount.compareTo(outLoan) > 0) {
                throw new XkdException("放款金额大于申请提款金额");
            }
            BeanUtil.copyProperties(outInfoDto, outInfo);
            outInfo.setOutStatus(SysConstant.OUT_INFO_PAY_TOW);
            outInfo.setStatus(SysConstant.BUSINESS_STATUS_TWELVE);
            updateById(outInfo);
            Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId));
            //插入订单表
            String order = OrderNumUtil.getOutAmountOrder();
            long i = loanAmount.multiply(new BigDecimal(100)).intValue();
            if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {//通道一
                OutOrderInfo outOrderInfo = new OutOrderInfo();
                outOrderInfo.setOrderId(outInfo.getOutId());
                outOrderInfo.setOrderNo(order);
                outOrderInfo.setAmount(i);
                outOrderInfo.setCompanyId(companyId);
                outOrderInfo.setIncomingId(incomingId);
                outOrderInfo.setOutId(outId);
                outOrderInfo.setToAccName(credit.getAccountName());
                outOrderInfo.setOutTime(new Date());
                outOrderInfo.setToAccNo(credit.getReceivingAccount());
                outOrderInfo.setToBankName(credit.getBank());
                outOrderInfo.setTransCardId(outInfoDto.getTransCardId());
                outOrderInfo.setTransCnap(outInfoDto.getToBankCode());
                String client_trans_id = "T" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
                outOrderInfo.setClientTransId(client_trans_id);
                outOrderInfoMapper.insert(outOrderInfo);
                //调拨提现---》成功----》放款
                apiService.outAmountAllot(companyId, loans_out_no, String.valueOf(loanAmount.multiply(new BigDecimal(100)).intValue()), client_trans_id);
            } else if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {//通道二
                ChannelPayTow channelPayTow = new ChannelPayTow();
                channelPayTow.setOrderNo(order);
                channelPayTow.setPayType("1");
                channelPayTow.setAmount(i);
                channelPayTow.setAccountType("1");
                channelPayTow.setPayStatus("1");
                channelPayTow.setIncomingId(incomingId);
                channelPayTow.setOutId(outId);
                channelPayTow.setCompanyId(companyId);
                channelPayTow.setCreateTime(new Date());
                channelPayTow.setToAccName(credit.getAccountName());
                channelPayTow.setToAccNo(credit.getReceivingAccount());
                channelPayTow.setToBankName(credit.getBank());
                channelPayTow.setTransCardId(outInfoDto.getTransCardId());
                channelPayTowMapper.insert(channelPayTow);

                OutAmountDto outAmountDto = new OutAmountDto();
                outAmountDto.setCompanyId(credit.getCompanyId());
                outAmountDto.setToBankName(credit.getBank());
                outAmountDto.setToAccName(credit.getAccountName());
                outAmountDto.setToAccNo(credit.getReceivingAccount());
                outAmountDto.setId(outInfo.getOutId());
                outAmountDto.setOutId(outId);
                outAmountDto.setTransCardId(outInfoDto.getTransCardId());
                outAmountDto.setIncomingId(incomingId);
                outAmountDto.setTransMoney(loanAmount + "");
                outAmountDto.setChannelType(outInfoDto.getPayPass());
                outAmountDto.setTransCnap(outInfoDto.getToBankCode());
                outAmountDto.setOrder(order);
                // 确保锁的释放在事务提交之后
                TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                    @Override
                    public void afterCommit() {//事务成功后执行
                        try {
                            dlApiService.outAmount(outAmountDto);
                        } catch (Exception e) {
                            throw new XkdException("放款失败");
                        }
                    }
                });
            }
        } catch (Exception e) {
            log.error("放款异常：{},异常信息: {}", e.getMessage(), e);
            throw new XkdException(e.getMessage());
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), transferOrderLock + companyId);
            }
            //放款后删除验证码
//            redisTemplate.delete("out_amount:" + outInfoDto.getOutTimestamp() + phone);
        }
    }

    //测试环境使用 --通道二
    public void testPassTowOutAmount(Integer incomingId, Integer outId, Integer companyId, long amount, String order) {
        String status = null;
        String incomingStatus = "";
        IncomingInfo incomingInfo = null;
        Integer productId = null;
        Product product = null;
        if (incomingId != null) {
            incomingInfo = incomingInfoMapper.selectById(incomingId);
            productId = incomingInfo.getProductId();
            product = productMapper.selectById(productId);
        }
        OutInfo outInfo = new OutInfo();
        incomingStatus = SysConstant.BUSINESS_STATUS_THIRTEEN;
        status = "2";
        outInfo.setPayTime(new Date());
        OutInfo outInfo1 = outInfoMapper.selectById(outId);
        Credit credit = creditMapper.selectByCredit(incomingId);
        List<RepaymentSchedule> schedules = new ArrayList<>();
        //保单贷：：入保单
        if (product.getType().equals(SysConstant.PRODUCT_FIVE)) {
            this.calculateInsuranceLoanSchedule(outInfo1, credit.getPostServiceFeeRate());
            List<CarCredit> carCredits = carCreditMapper.selectList(new LambdaQueryWrapper<CarCredit>()
                    .eq(CarCredit::getIncomingId, incomingId)
                    .ge(CarCredit::getEndTime, new Date()));
            List<GuaranteeSlip> guaranteeSlips = BeanUtil.copyToList(carCredits, GuaranteeSlip.class);
            if (CollectionUtil.isNotEmpty(guaranteeSlips)) {
                guaranteeSlipMapper.batchInsert(guaranteeSlips);
            }
        } else {
            schedules = this.calculateLoanSchedule(outInfo1);
        }
        String incomingOrder = incomingInfo.getIncomingOrder();
        //扣除合同服务费
        iCompanyService.deductingBalanceMain(null, null, incomingId, null, SysConstant.BUSINESS_FEE_SEVEN, null, null, null);
        //扣除放款服务费
        iCompanyService.deductingBalanceMain(outInfo1, incomingOrder, incomingId, null, SysConstant.BUSINESS_FEE_FIVE, null, null, null);
        try {
            //扣除产品服务费
            iCompanyService.deductingBalanceMain(outInfo1, null, incomingId, null, SysConstant.BUSINESS_FEE_ONE, null, null, null);
        } catch (Exception e) {
            log.info("放款回调，放款金额未在设置阶梯收费范围内");
        }
        //发送短信
//
//        String format = dlApiServiceimpl.sendText(outInfo1, incomingInfo.getName(), credit);
//        try {
//            if (!product.getType().equals(SysConstant.PRODUCT_FIVE)) {
//                log.info("发送放款到账短信");
//                msmService.sendH5(AESUtil.decrypt(incomingInfo.getPhone(), aesKey), format, format);
//            }
//        } catch (Exception e) {
//            log.info("发送放款到账短信失败");
//        }

        //修改宝付余额
        //放款账户
        companyMapper.updateTowBaofuBalance(companyId, amount);
        if (product.getType().equals(SysConstant.PRODUCT_FIVE)) {
            incomingInfoMapper.updateStatus(incomingStatus, incomingId);
        } else {
            outInfo.setStatus(incomingStatus);
        }
        //修改放款表状态
        outInfo.setOutStatus(status);
        outInfo.setTransOrderId("测试");
        LambdaQueryWrapper<OutInfo> lqw = new LambdaQueryWrapper<>();
        lqw.eq(OutInfo::getOutId, outId);
        //修改放款状态
        outInfoMapper.update(outInfo, lqw);
        ChannelPayTow o = new ChannelPayTow();
        o.setPayStatus(status);
        o.setTradeTime(new Date());
        LambdaQueryWrapper<ChannelPayTow> lqw2 = new LambdaQueryWrapper<>();
        lqw2.eq(ChannelPayTow::getOrderNo, order);
        channelPayTowMapper.update(o, lqw2);

        //如果是第三方的数据 回调对方
        if (incomingInfo.getIncomingType().equals("2"))
            iAsyncService.advanceOutAmount(order, companyId, status, "ceshi", JSONUtil.toJsonStr(schedules));
        log.info("渠道二代付回调函数执行完毕");
    }

    //测试环境使用--通道一
    public void testOutAmount(Integer incomingId, Integer outId, Integer companyId, long amount, String order) {
        OutInfo outInfo = new OutInfo();
        OutOrderInfo o = new OutOrderInfo();
        String status = SysConstant.BUSINESS_STATUS_THIRTEEN;
        String outStatus = "";
        String payStatus = "";
        outInfo.setPayTime(new Date());
        outStatus = "2";
        payStatus = "2";
        IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
        Integer productId = incomingInfo.getProductId();
        Product product = productMapper.selectById(productId);
        Credit credit = creditMapper.selectByCredit(incomingId);

        OutInfo outInfo1 = outInfoMapper.selectOne(new LambdaQueryWrapper<OutInfo>().eq(OutInfo::getOutId, outId));
        //保单贷：：入保单
        if (product.getType().equals(SysConstant.PRODUCT_FIVE)) {
            calculateInsuranceLoanSchedule(outInfo1, credit.getPostServiceFeeRate());
            List<CarCredit> carCredits = carCreditMapper.selectList(new LambdaQueryWrapper<CarCredit>()
                    .eq(CarCredit::getIncomingId, incomingId)
                    .ge(CarCredit::getEndTime, new Date()));
            List<GuaranteeSlip> guaranteeSlips = BeanUtil.copyToList(carCredits, GuaranteeSlip.class);
            if (CollectionUtil.isNotEmpty(guaranteeSlips)) {
                guaranteeSlipMapper.batchInsert(guaranteeSlips);
            }
        } else {
            calculateLoanSchedule(outInfo1);
        }
        //扣除放款服务费
        String incomingOrder = incomingInfo.getIncomingOrder();
        iCompanyService.deductingBalanceMain(outInfo1, incomingOrder, incomingId, null, SysConstant.BUSINESS_FEE_FIVE, null, null, null);
        try {
            //扣除产品服务费
            iCompanyService.deductingBalanceMain(outInfo1, null, incomingId, null, SysConstant.BUSINESS_FEE_ONE, null, null, null);
        } catch (Exception e) {
            throw new XkdException("放款金额未在设置阶梯收费范围内");
        }
//        扣除合同服务费
        iCompanyService.deductingBalanceMain(null, null, incomingId, null, SysConstant.BUSINESS_FEE_SEVEN, null, null, null);
        //修改宝付余额
        companyMapper.updateBaofuBalance(companyId, amount);
        //发送短信
//        String format = dlApiServiceimpl.sendText(outInfo1, incomingInfo.getName(), credit);
//        try {
//            log.info("发送测试放款到账短信");
//            msmService.sendH5(AESUtil.decrypt(incomingInfo.getPhone(), aesKey), format, format);
//        } catch (Exception e) {
//            log.info("发送测试放款到账短信失败");
//        }
        outInfo.setOutStatus(outStatus);
        LambdaQueryWrapper<OutInfo> lqw = new LambdaQueryWrapper<>();
        lqw.eq(OutInfo::getOutId, outId);
        if (product.getType().equals(SysConstant.PRODUCT_FIVE)) {//除了保单贷，其他进件流程只到授信完成
            incomingInfoMapper.updateStatus(status, incomingId);
        } else {
            outInfo.setStatus(status);
        }
        //修改放款状态
        outInfoMapper.update(outInfo, lqw);
        log.info("放款回调函数执行完毕");
        //修改放款订单状态
        o.setTradeTime(new Date());
        o.setPayStatus(payStatus);
        LambdaQueryWrapper<OutOrderInfo> lqw2 = new LambdaQueryWrapper<>();
        lqw2.eq(OutOrderInfo::getOrderNo, order);
        outOrderInfoMapper.update(o, lqw2);
        log.info("放款回调函数执行完毕");
    }

    private String checkOutCode(OutInfoDto outInfoDto, Integer companyId) {
        //主账号
        SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getCompanyId, companyId).eq(SysUser::getUserType, SysConstant.USER_TYPE_P));
        String phone = sysUser.getPhone();
        String code = (String) redisTemplate.opsForValue().get("out_amount:" + outInfoDto.getOutTimestamp() + phone);
        // 判断验证码是否一致
        if (StringUtils.isEmpty(code)) {
            throw new BadCredentialsException("放款验证码失效");
        }
        if (!outInfoDto.getCode().equals(code)) {
            throw new BadCredentialsException("放款验证码错误");
        }
        return phone;
    }

    @Override
    public OutInfoWindowVo getOutDetail1(Integer id) {
        OutInfoWindowVo outInfoWindowVo = incomingInfoMapper.getDetail1(id);
        Calendar loanStartCalendar = Calendar.getInstance();
        Calendar loanStartCalendar1 = Calendar.getInstance();
        Date date = new Date();
        Integer term = outInfoWindowVo.getTerm();
        String repayDay = outInfoWindowVo.getRepayDay();
        //按月
        if ("按月".equals(outInfoWindowVo.getRepayFrequency())) {
            loanStartCalendar1.add(Calendar.MONTH, 1);
            loanStartCalendar1.set(Calendar.DAY_OF_MONTH, Integer.parseInt(repayDay));

            loanStartCalendar.add(Calendar.MONTH, term);
            loanStartCalendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(repayDay));
            // 如果还款日是1号，并且今天是月底（比如30或31号），则多推一个月
            if ("1".equals(repayDay)) {
                Calendar today = Calendar.getInstance();
                int currentDay = today.get(Calendar.DAY_OF_MONTH);
                int lastDayOfMonth = today.getActualMaximum(Calendar.DAY_OF_MONTH);
                if (currentDay == lastDayOfMonth) {
                    loanStartCalendar.add(Calendar.MONTH, 1); // 多推一个月
                }
            }
            outInfoWindowVo.setOutEndTime(loanStartCalendar.getTime());
            outInfoWindowVo.setLatestRepayDate(loanStartCalendar1.getTime());

        } else if ("按周".equals(outInfoWindowVo.getRepayFrequency())) {
            int termUse = outInfoWindowVo.getTermUse(); // 总贷款周数
            int repayDayNum = Integer.parseInt(repayDay); // 用户输入还款日（1-7：周一至周日）
            int repayDayCalendar; // 转换为Calendar.DAY_OF_WEEK（1=周日,2=周一,...,7=周六）

            // 修正：支持周六（6→7）和周日（7→1）的Calendar转换
            if (repayDayNum == 7) {
                // 周日特殊处理：用户输入7 → Calendar.SUNDAY=1
                repayDayCalendar = Calendar.SUNDAY;
            } else {
                // 周一至周六：用户输入1-6 → 2-7（Calendar.MONDAY=2至SATURDAY=7）
                repayDayCalendar = repayDayNum + 1;
            }

            // 获取当前日期的周几（1=周日,2=周一,...,7=周六）
            Calendar today = Calendar.getInstance();
            int currentDayOfWeek = today.get(Calendar.DAY_OF_WEEK);

            // 今天不是还款日时，结束日期减少1周（含周末还款日场景）
            if (currentDayOfWeek != repayDayCalendar) {
                termUse -= 1;
            }

            // 计算结束日期（原有逻辑保持，支持周末还款日）
            Calendar loanEndCalendar = Calendar.getInstance();
            loanEndCalendar.add(Calendar.WEEK_OF_MONTH, termUse);
            int endDayOfWeek = loanEndCalendar.get(Calendar.DAY_OF_WEEK);
            int endDaysAdjust = (repayDayCalendar - endDayOfWeek + 7) % 7;
            loanEndCalendar.add(Calendar.DAY_OF_YEAR, endDaysAdjust);
            outInfoWindowVo.setOutEndTime(loanEndCalendar.getTime());

            // 修正最近还款日计算（支持周末还款日）
            Calendar latestRepayCalendar = Calendar.getInstance();
            int daysToNextRepay = (repayDayCalendar - currentDayOfWeek + 7) % 7;
            // 若今天是还款日（含周六/周日），则顺延至下一周
            if (daysToNextRepay == 0) {
                daysToNextRepay = 7;
            }
            latestRepayCalendar.add(Calendar.DAY_OF_YEAR, daysToNextRepay);
            outInfoWindowVo.setLatestRepayDate(latestRepayCalendar.getTime());

        } else {
            //按日
            loanStartCalendar.add(Calendar.DAY_OF_MONTH, outInfoWindowVo.getTermUse());
            outInfoWindowVo.setOutEndTime(loanStartCalendar.getTime());
            outInfoWindowVo.setLatestRepayDate(loanStartCalendar.getTime());
        }
        outInfoWindowVo.setOutStartTime(date);
        outInfoWindowVo.setAccountPayment(outInfoWindowVo.getPayPass().equals("1") ? passOneAccount : passTwoAccount);
        return outInfoWindowVo;
    }


    /**
     * @description: 业务账户-放款记录
     * @author: ZCH
     * @date: 2025/3/7 0007 14:46
     * @param: [dto]
     * @return: com.github.pagehelper.Page<com.xd.core.finance.vo.LendingRecordQueryVo>
     **/


    @Override
    public Page<LendingRecordQueryVo> getLendingRecords(LendingRecordQueryDto dto) {
        String companyType = SecurityUtil.getLoginUser().getUser().getCompanyType();
        Integer companyId = null;
        List<Integer> userIds = null;
        if (companyType.equals(SysConstant.USER_COMPANY_TYPE_ONE) || companyType.equals(SysConstant.USER_COMPANY_TYPE_FOUR)) {
            companyId = dto.getCompanyId();
        } else if (companyType.equals(SysConstant.USER_COMPANY_TYPE_TOW)) {
            companyId = SecurityUtil.getLoginCompanyId();
            userIds = iSysUserService.getUserIdsByGroup();
        } else if (companyType.equals(SysConstant.USER_COMPANY_TYPE_THREE)) {//代理
            return null;
        }
        //开始分页
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        //判断通道
        if (dto.getPayPass().equals("1")) {
            //通道一放款记录
            Page<LendingRecordQueryVo> recordsChannelOne = outInfoMapper.getLendingRecords(dto, userIds, companyId);
            return recordsChannelOne;
        } else {
            //通道二放款记录
            Page<LendingRecordQueryVo> recordsChannelTwo = channelPayTowMapper.getLendingRecords(dto, userIds, companyId);
            return recordsChannelTwo;
        }
    }

    @Override
    public void exportIncomingInfoList(OutInfoQuery query, Integer id) {
        String name = "放款excel文件" + UUID.randomUUID().toString().replace("-", "");
        Integer companyId = SecurityUtil.getLoginCompanyId();
        FileOutputStream fileOutputStream = null;
        MissionCenterInfo missionCenterInfo = new MissionCenterInfo();
        missionCenterInfo.setId(id);
        missionCenterInfo.setFileName(name + ".xlsx");

        String filePath = localPathDir + exportFile + "/" + companyId;
        File dirFile = new File(filePath);
        if (!dirFile.exists()) {
            dirFile.mkdirs();
        }

        try {
            List<RowHeightColWidthModel> rowHeightColWidthList = new ArrayList<>();
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 0, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 1, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 2, 10));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 3, 15));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 4, 10));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 5, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 6, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 7, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 8, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 9, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 10, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 11, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 12, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 13, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 14, 20));
            File file = new File(filePath + "/" + name + ".xlsx");
            fileOutputStream = new FileOutputStream(file);

            ExcelWriter write = EasyExcel.write(fileOutputStream, OutInfoPageVo.class).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(name).registerWriteHandler(new CustomRowHeightColWidthHandler(rowHeightColWidthList)).build();
            query.setPageSize(0);
            query.setStatus("");
            List<OutInfoPageVo> list = getList(query);
            for (OutInfoPageVo customerInfoVo : list) {
                String decryptPhone = AESUtil.decrypt(customerInfoVo.getPhone(), aesKey);
                customerInfoVo.setPhone(decryptPhone);
            }
            write.write(list, writeSheet);
            write.finish();
            fileOutputStream.flush();
            //修改文件状态 导出成功
            missionCenterInfo.setFileStatus(SysConstant.YSZT_FILE_STATUS_TOW);
            missionCenterInfo.setFileUrl(exportFile + "/" + companyId + "/" + name + ".xlsx");
            missionCenterInfoMapper.updateById(missionCenterInfo);
        } catch (Exception e) {
            //修改文件状态 导出失败
            missionCenterInfo.setFileStatus(SysConstant.YSZT_FILE_STATUS_THREE);
            missionCenterInfoMapper.updateById(missionCenterInfo);
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    @Override
    public OrderDetailIncomingInfoVo getIncomingDetail(Integer id) {
        OrderDetailIncomingInfoVo detail = incomingInfoMapper.getDetail2(id);
        detail.setOutIdTimes(outInfoMapper.selectIdTimes(id));
        return detail;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void againOutInfo(OutInfoDto outInfoDto) throws Exception {
        Integer incomingId = outInfoDto.getIncomingId();
        Integer outId = outInfoDto.getOutId();
        IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
        OutInfo outInfo = outInfoMapper.selectById(outId);
        if (SysConstant.ORDER_DISABLE.equals(incomingInfo.getIsEnable())) {
            throw new XkdException("该订单已被平台停用");
        }
        //判断资方账户是否被冻结
        Integer companyId = outInfoDto.getCompanyId();
        Company company = companyMapper.selectById(companyId);
        if ("1".equals(company.getIsFreeze())) {
            throw new XkdException("账户已被冻结");
        }
        if (!active.equals("prd"))
            throw new XkdException("测试环境不允许操作");
        CompanyAccount companyAccount = companyAccountMapper.selectById(companyId);
        if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
            if (ObjectUtil.isNotEmpty(companyAccount) && companyAccount.getPrestoreMemberId().isEmpty()) {
                throw new XkdException("请绑定线下分账商户号");
            }
        }
        String phone = checkOutCode(outInfoDto, companyId);
        RLock lock = redissonClient.getLock(transferOrderLock + companyId);
        String lockName = SysConstant.FEE_LOCK + companyId;
        RLock lock2 = redissonClient.getLock(lockName);
        try {
            log.info("尝试获取锁: {}", lockName);
            lock2.lock();
            log.info("成功获取锁: {}", lockName);
            //判断服务费是为负
            BigDecimal bigDecimal = companyMapper.selectBalance(companyId);
            if (bigDecimal.compareTo(BigDecimal.ZERO) < 0)
                throw new XkdException("服务费不足，无法放款");
        } catch (Exception e) {
            throw new XkdException(e.getMessage());
        } finally {
            if (lock2.isHeldByCurrentThread()) {
                lock2.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), lockName);
            }
        }
        try {
            log.info("尝试获取锁: {}", transferOrderLock + companyId);
            lock.lock();
            log.info("成功获取锁: {}", transferOrderLock + companyId);
            //查询放款账户余额是否充足
            List<String> collect = Arrays.stream(company.getThoroughFare().split(",")).map(String::valueOf).collect(Collectors.toList());
            if (!collect.contains(outInfoDto.getPayPass())) {
                throw new XkdException("通道类型错误");
            }
            long oa = 0;
            if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
                oa = company.getOutBalance() - company.getOutBalanceFreeze();
            } else if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {
                oa = company.getTowBaofuBalance() - company.getTowOutBalanceFreeze();
            }
            //查询是否绑卡
            String cardStatus = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, outInfoDto.getIncomingId())).getCardStatus();
            if ("0".equals(cardStatus)) {
                throw new XkdException("还款人未绑定银行卡");
            }
            if (outInfoDto.getLoanAmount().compareTo(new BigDecimal(oa).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)) > 0)
                throw new XkdException("放款账户余额不足");
            if (ObjectUtil.isNotEmpty(outInfo) && !(SysConstant.OUT_INFO_PAY_FOUR.equals(outInfo.getOutStatus()) || SysConstant.OUT_INFO_PAY_FIVE.equals(outInfo.getOutStatus()))) {
                throw new XkdException("放款状态异常,不允许重新放款");
            }
            BigDecimal loanAmount = outInfoDto.getLoanAmount();

            Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId));
            BeanUtil.copyProperties(outInfoDto, outInfo);
            outInfo.setOutLoan(loanAmount);
            outInfo.setOutStatus(SysConstant.OUT_INFO_PAY_TOW);
            updateById(outInfo);

            OutAmountDto outAmountDto = new OutAmountDto();
            outAmountDto.setCompanyId(credit.getCompanyId());
            outAmountDto.setToBankName(credit.getBank());
            outAmountDto.setToAccName(credit.getAccountName());
            outAmountDto.setToAccNo(credit.getReceivingAccount());
            outAmountDto.setId(outInfo.getOutId());
            outAmountDto.setOutId(outId);
            outAmountDto.setTransCardId(outInfoDto.getTransCardId());
            outAmountDto.setIncomingId(incomingId);
            outAmountDto.setTransMoney(loanAmount + "");
            outAmountDto.setChannelType(outInfoDto.getPayPass());
            outAmountDto.setTransCnap(outInfoDto.getToBankCode());
            //插入订单表
            String order = OrderNumUtil.getOutAmountOrder();
            long i = new BigDecimal(outAmountDto.getTransMoney()).multiply(new BigDecimal(100)).intValue();

            if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
                OutOrderInfo outOrderInfo = new OutOrderInfo();
                outOrderInfo.setOrderId(outInfo.getOutId());
                outOrderInfo.setOrderNo(order);
                outOrderInfo.setAmount(i);
                outOrderInfo.setCompanyId(companyId);
                outOrderInfo.setIncomingId(incomingId);
                outOrderInfo.setToAccName(outAmountDto.getToAccName());
                outOrderInfo.setToAccNo(outAmountDto.getToAccNo());
                outOrderInfo.setOutTime(new Date());
                outOrderInfo.setOutId(outId);
                outOrderInfo.setTransCnap(outInfoDto.getToBankCode());
                outOrderInfo.setToBankName(outAmountDto.getToBankName());
                outOrderInfo.setTransCardId(outAmountDto.getTransCardId());
                outOrderInfoMapper.insert(outOrderInfo);
            } else if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {
                ChannelPayTow channelPayTow = new ChannelPayTow();
                channelPayTow.setOrderNo(order);
                channelPayTow.setPayType("1");
                channelPayTow.setAmount(i);
                channelPayTow.setAccountType("1");
                channelPayTow.setPayStatus("1");
                channelPayTow.setIncomingId(incomingId);
                channelPayTow.setOutId(outId);
                channelPayTow.setCompanyId(companyId);
                channelPayTow.setCreateTime(new Date());
                channelPayTow.setToAccName(credit.getAccountName());
                channelPayTow.setToAccNo(credit.getReceivingAccount());
                channelPayTow.setToBankName(credit.getBank());
                channelPayTow.setTransCardId(outInfoDto.getTransCardId());
                channelPayTowMapper.insert(channelPayTow);
            }

            outAmountDto.setOrder(order);
            // 确保锁的释放在事务提交之后
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {//事务成功后执行
                    try {
                        dlApiService.outAmount(outAmountDto);
                    } catch (Exception e) {
                        throw new XkdException("下一期扣款错误");
                    }
                }
            });
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), transferOrderLock + companyId);
            }
            //放款后删除验证码
            redisTemplate.delete("out_amount:" + outInfoDto.getOutTimestamp() + phone);
        }

    }

    @Override
    public OutCounts getOutCounts(HomeQueryDto query) {
        String companyType = SecurityUtil.getLoginCompany().getCompanyType();
        Integer companyId = SecurityUtil.getLoginCompanyId();

        if (StringUtils.isEmpty(query.getStartTime()) && StringUtils.isEmpty(query.getEndTime())) {
            LocalDate today = LocalDate.now();
            LocalDate oneMonthAgo = today.minusMonths(1);

            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

            query.setStartTime(oneMonthAgo.format(formatter));
            query.setEndTime(today.format(formatter));

        }
        return outInfoMapper.getOutCounts(query, companyId, companyType);
    }

    /**
     * @description: 业务账户放款记录导出
     * @author: ZCH
     * @date: 2025/3/25 0025 10:51
     * @param: [dto, id]
     * @return: void
     **/
    @Override
    public void exportLendingRecords(LendingRecordQueryDto dto, Integer id) {
        String name = "";
        if ("1".equals(dto.getPayPass())) {
            name = "通道一放款记录excel文件" + UUID.randomUUID().toString().replace("-", "");
        } else {
            name = "官方通道（标准）放款记录excel文件" + UUID.randomUUID().toString().replace("-", "");
        }
        Integer companyId = SecurityUtil.getLoginCompanyId();
        FileOutputStream fileOutputStream = null;
        MissionCenterInfo missionCenterInfo = new MissionCenterInfo();
        missionCenterInfo.setId(id);
        missionCenterInfo.setFileName(name + ".xlsx");
        String filePath = localPathDir + exportFile + "/" + companyId;
        File dirFile = new File(filePath);
        if (!dirFile.exists()) {
            dirFile.mkdirs();
        }
        try {
            List<RowHeightColWidthModel> rowHeightColWidthList = new ArrayList<>();
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 0, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 1, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 2, 10));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 3, 15));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 4, 10));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 5, 10));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 6, 10));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 7, 10));
            File file = new File(filePath + "/" + name + ".xlsx");
            fileOutputStream = new FileOutputStream(file);

            ExcelWriter write = EasyExcel.write(fileOutputStream, LendingRecordQueryVo.class).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(name).registerWriteHandler(new CustomRowHeightColWidthHandler(rowHeightColWidthList)).build();
            dto.setPageSize(0);
            List<LendingRecordQueryVo> list = getLendingRecords(dto);
            write.write(list, writeSheet);
            write.finish();
            fileOutputStream.flush();
            //修改文件状态 导出成功
            missionCenterInfo.setFileStatus(SysConstant.YSZT_FILE_STATUS_TOW);
            missionCenterInfo.setFileUrl(exportFile + "/" + companyId + "/" + name + ".xlsx");
            missionCenterInfoMapper.updateById(missionCenterInfo);
        } catch (Exception e) {
            //修改文件状态 导出失败
            missionCenterInfo.setFileStatus(SysConstant.YSZT_FILE_STATUS_THREE);
            missionCenterInfoMapper.updateById(missionCenterInfo);
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void updateAccount(AccountDto accountDto) {
        Integer incomingId = accountDto.getIncomingId();
        int count = count(new LambdaQueryWrapper<OutInfo>().eq(OutInfo::getIncomingId, incomingId).in(OutInfo::getOutStatus, "1", "2"));
        if (count > 0) {
            throw new XkdException("用户放款后,不允许修改收款账户");
        }
        Credit credit = iCreditService.getOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId));
        String oldReceivingAccount = credit.getReceivingAccount();
        String oldBank = credit.getBank();
        if (!oldReceivingAccount.equals(accountDto.getReceivingAccount()) || !oldBank.equals(accountDto.getBank())) {
            List<Contract> contracts = contractMapper.selectList(new LambdaQueryWrapper<Contract>().eq(Contract::getIncomingId, incomingId).eq(Contract::getType, SysConstant.CONTRACT_TYPE_TOW));
            if (CollectionUtils.isNotEmpty(contracts)) {
                contracts.forEach(contract -> {//补充协议
                    iAsyncService.extraBorrowing(accountDto.getIncomingId(), accountDto.getBank(), accountDto.getReceivingAccount(), contract.getOutId(), credit);
                });
            }
        }
        CardProtocolInfo cardProtocolInfo = cardProtocolInfoMapper.selectOne
                (new LambdaQueryWrapper<CardProtocolInfo>().eq(CardProtocolInfo::getRepayAccount, accountDto.getReceivingAccount()));
        if (ObjectUtil.isNull(cardProtocolInfo)) {
            credit.setCardStatus("0");
        } else {
            credit.setCardStatus("1");
        }
        UpdateWrapper<Credit> wrapper = Wrappers.<Credit>update()
                .set("account_name", accountDto.getAccountName())
                .set("bank", accountDto.getBank())
                .set("card_status", credit.getCardStatus())
                .set("receiving_account", accountDto.getReceivingAccount())
                .set("repay_name", accountDto.getAccountName())
                .set("repay_account", accountDto.getReceivingAccount())
                .set("repay_bank", accountDto.getBank())
                .set("bank_phone", accountDto.getBankPhone())
                .eq("incoming_id", incomingId);
        iCreditService.update(wrapper);
    }

    @Override
    public Page<OutInsuranceInfoPageVo> outInsuranceInfoList(OutInfoQuery query) {
        String companyType = SecurityUtil.getLoginUser().getUser().getCompanyType();
        Integer companyId = query.getCompanyId();
        List<Integer> userIds = null;
        if (!companyType.equals(SysConstant.USER_COMPANY_TYPE_ONE) && !companyType.equals(SysConstant.USER_COMPANY_TYPE_FOUR)) {//不是平台和集团
            companyId = SecurityUtil.getLoginCompanyId();
            userIds = iSysUserService.getUserIdsByGroup();
        }
        PageHelper.startPage(query.getPageNum(), query.getPageSize());
        return (Page<OutInsuranceInfoPageVo>) outInfoMapper.outInsuranceInfoList(query, userIds, companyId, companyType);
    }

    @Override
    public OutInsuranceInfoVo getInsuranceIncomingDetail(Integer id) {
        return outInfoMapper.getInsuranceIncomingDetail(id);
    }

    @Override
    public List<OutInsuranceRecordVo> getOutOrderList(Integer incomingId, String payPass) {
        if (SysConstant.CHANNEL_TYPE_ONE.equals(payPass)) {
            return outOrderInfoMapper.getOutOrderList(incomingId);
        } else {
            return channelPayTowMapper.getOutOrderList(incomingId);
        }
    }

    @Override
    public OutInsuranceWindowVo getInsuranceOutWindow(Integer id) {
        OutInsuranceWindowVo outInfoWindowVo = incomingInfoMapper.getInsuranceOutWindow(id);
        Calendar loanStartCalendar = Calendar.getInstance();
        Calendar loanStartCalendar1 = Calendar.getInstance();
        Date date = new Date();
        Integer term = outInfoWindowVo.getTerm();
        String repayDay = outInfoWindowVo.getRepayDay();
        loanStartCalendar1.add(Calendar.MONTH, 1);
        loanStartCalendar1.set(Calendar.DAY_OF_MONTH, Integer.parseInt(repayDay));
        loanStartCalendar.add(Calendar.MONTH, term);
        loanStartCalendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(repayDay));
        // 如果还款日是1号，并且今天是月底（比如30或31号），则多推一个月
        if ("1".equals(repayDay)) {
            Calendar today = Calendar.getInstance();
            int currentDay = today.get(Calendar.DAY_OF_MONTH);
            int lastDayOfMonth = today.getActualMaximum(Calendar.DAY_OF_MONTH);
            if (currentDay == lastDayOfMonth) {
                loanStartCalendar.add(Calendar.MONTH, 1); // 多推一个月
            }
        }
        outInfoWindowVo.setOutEndTime(loanStartCalendar.getTime());
        outInfoWindowVo.setLatestRepayDate(loanStartCalendar1.getTime());


        outInfoWindowVo.setOutStartTime(date);
        outInfoWindowVo.setAccountPayment(outInfoWindowVo.getPayPass().equals("1") ? passOneAccount : passTwoAccount);
        return outInfoWindowVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void insertInsuranceOutInfo(OutInfoDto outInfoDto) throws Exception {
        //判断资方账户是否被冻结
        Integer companyId = outInfoDto.getCompanyId();
        Company company = companyMapper.selectById(companyId);
        if ("1".equals(company.getIsFreeze())) {
            throw new XkdException("账户已被冻结");
        }
        if (!active.equals("prd")) {
            testAddOutInfobaodan(outInfoDto);
            return;
        }
        Integer incomingId = outInfoDto.getIncomingId();
        Integer outId = outInfoDto.getOutId();
        OutInfo outInfo = outInfoMapper.selectById(outId);
        IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
        if (SysConstant.ORDER_DISABLE.equals(incomingInfo.getIsEnable())) {
            throw new XkdException("该订单已被平台停用");
        }
        //
        CompanyAccount companyAccount = companyAccountMapper.selectById(companyId);
        if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {//通道一验证
            if (ObjectUtil.isNotEmpty(companyAccount) && companyAccount.getPrestoreMemberId().isEmpty()) {
                throw new XkdException("请绑定线下分账商户号");
            }
        }
        //校验验证码
        String phone = checkOutCode(outInfoDto, companyId);

        RLock lock = redissonClient.getLock(transferOrderLock + companyId);
        String lockName = SysConstant.FEE_LOCK + companyId;
        RLock lock2 = redissonClient.getLock(lockName);
        try {
            log.info("尝试获取锁: {}", lockName);
            lock2.lock();
            log.info("成功获取锁: {}", lockName);
            company = companyMapper.selectById(companyId);
            List<String> collect = Arrays.stream(company.getThoroughFare().split(",")).map(String::valueOf).collect(Collectors.toList());
            if (!collect.contains(outInfoDto.getPayPass())) {
                throw new XkdException("通道类型错误");
            }
            //判断服务费是为负
            String accountingType = company.getAccountingType();
            //判断
            BigDecimal balance = BigDecimal.ZERO;
            if (!SysConstant.ACCOUNTING_TYPE_ONE.equals(accountingType)) {
                //集团服务费余额
                balance = companyMapper.selectOne(new LambdaQueryWrapper<Company>().eq(Company::getId, company.getBlocId())).getBalance();
            } else {
                //资方服务费余额
                balance = company.getBalance();
            }
            if (balance.compareTo(BigDecimal.ZERO) < 0)
                throw new XkdException("服务费不足，无法放款");
        } catch (Exception e) {
            log.error("放款异常：{},异常信息: {}", e.getMessage(), e);
            throw new XkdException(e.getMessage());
        } finally {
            if (lock2.isHeldByCurrentThread()) {
                lock2.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), lockName);
            }
        }
        try {
            log.info("尝试获取锁: {}", transferOrderLock + companyId);
            lock.lock();
            log.info("成功获取锁: {}", transferOrderLock + companyId);
            //查询是否绑卡
            String cardStatus = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, outInfoDto.getIncomingId())).getCardStatus();
            if ("0".equals(cardStatus)) {
                throw new XkdException("还款人未绑定银行卡");
            }
            //校验合同是否签署完毕
            Integer count = contractMapper.selectCount(new LambdaQueryWrapper<Contract>()
                    .eq(Contract::getOutId, outInfo.getOutId())
                    .eq(Contract::getType, SysConstant.CONTRACT_TYPE_TWELVE)
                    .eq(Contract::getStatus, SysConstant.CONTRACT_DO));
            if (count != 1) {
                throw new XkdException("用户合同未签署完毕");
            }
            if (SysConstant.BUSINESS_STATUS_THIRTEEN.equals(incomingInfo.getStatus()) || SysConstant.BUSINESS_STATUS_TWELVE.equals(incomingInfo.getStatus())) {
                throw new XkdException("业务状态异常");
            }
            //查询放款账户余额是否充足
            long oa = 0;//剩余余额
            if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
                oa = company.getOutBalance() - company.getOutBalanceFreeze();
            } else if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {
                oa = company.getTowBaofuBalance() - company.getTowOutBalanceFreeze();
            }
            if (outInfoDto.getLoanAmount().compareTo(new BigDecimal(oa).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)) > 0)
                throw new XkdException("放款账户余额不足");

            BigDecimal loanAmount = outInfoDto.getLoanAmount();
            BeanUtil.copyProperties(outInfoDto, outInfo);
            outInfo.setOutLoan(loanAmount);
            outInfo.setOutStatus(SysConstant.OUT_INFO_PAY_TOW);
            incomingInfoMapper.updateStatus(SysConstant.BUSINESS_STATUS_TWELVE, incomingId);
            updateById(outInfo);
            Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId));
            //插入订单表
            String order = OrderNumUtil.getOutAmountOrder();
            long i = loanAmount.multiply(new BigDecimal(100)).intValue();
            if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {//通道一
                OutOrderInfo outOrderInfo = new OutOrderInfo();
                outOrderInfo.setOrderId(outInfo.getOutId());
                outOrderInfo.setOrderNo(order);
                outOrderInfo.setAmount(i);
                outOrderInfo.setCompanyId(companyId);
                outOrderInfo.setIncomingId(incomingId);
                outOrderInfo.setOutId(outId);
                outOrderInfo.setToAccName(outInfoDto.getToAccName());
                outOrderInfo.setOutTime(new Date());
                outOrderInfo.setToAccNo(outInfoDto.getToAccNo());
                outOrderInfo.setTransCnap(outInfoDto.getToBankCode());
                outOrderInfo.setToBankName(outInfoDto.getBank());
                outOrderInfo.setTransCardId(outInfoDto.getTransCardId());
                String client_trans_id = "T" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
                outOrderInfo.setClientTransId(client_trans_id);
                outOrderInfoMapper.insert(outOrderInfo);
                //调拨提现---》成功----》放款
                apiService.outAmountAllot(companyId, loans_out_no, String.valueOf(loanAmount.multiply(new BigDecimal(100)).intValue()), client_trans_id);
            } else if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {//通道二
                ChannelPayTow channelPayTow = new ChannelPayTow();
                channelPayTow.setOrderNo(order);
                channelPayTow.setPayType("1");
                channelPayTow.setAmount(i);
                channelPayTow.setAccountType("1");
                channelPayTow.setPayStatus("1");
                channelPayTow.setIncomingId(incomingId);
                channelPayTow.setOutId(outId);
                channelPayTow.setCompanyId(companyId);
                channelPayTow.setCreateTime(new Date());
                channelPayTow.setToAccName(outInfoDto.getToAccName());
                channelPayTow.setToAccNo(outInfoDto.getToAccNo());
                channelPayTow.setToBankName(outInfoDto.getBank());
                channelPayTow.setTransCardId(outInfoDto.getTransCardId());
                channelPayTowMapper.insert(channelPayTow);

                OutAmountDto outAmountDto = new OutAmountDto();
                outAmountDto.setCompanyId(credit.getCompanyId());
                outAmountDto.setToBankName(outInfoDto.getBank());
                outAmountDto.setToAccName(outInfoDto.getToAccName());
                outAmountDto.setToAccNo(outInfoDto.getToAccNo());
                outAmountDto.setId(outInfo.getOutId());
                outAmountDto.setOutId(outId);
                outAmountDto.setTransCnap(outInfoDto.getToBankCode());
                outAmountDto.setTransCardId(outInfoDto.getTransCardId());
                outAmountDto.setIncomingId(incomingId);
                outAmountDto.setTransMoney(loanAmount + "");
                outAmountDto.setChannelType(outInfoDto.getPayPass());
                outAmountDto.setOrder(order);
                // 确保锁的释放在事务提交之后
                TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                    @Override
                    public void afterCommit() {//事务成功后执行
                        try {
                            dlApiService.outAmount(outAmountDto);
                        } catch (Exception e) {
                            throw new XkdException("放款失败");
                        }
                    }
                });
            }
        } catch (Exception e) {
            log.error("放款异常：{},异常信息: {}", e.getMessage(), e);
            throw new XkdException(e.getMessage());
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), transferOrderLock + companyId);
            }
            //放款后删除验证码
            redisTemplate.delete("out_amount:" + outInfoDto.getOutTimestamp() + phone);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void InsuranceAgainOutInfo(OutInfoDto outInfoDto) throws Exception {
        Integer incomingId = outInfoDto.getIncomingId();
        Integer outId = outInfoDto.getOutId();
        IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
        OutInfo outInfo = outInfoMapper.selectById(outId);
        if (SysConstant.ORDER_DISABLE.equals(incomingInfo.getIsEnable())) {
            throw new XkdException("该订单已被平台停用");
        }
        //判断资方账户是否被冻结
        Integer companyId = outInfoDto.getCompanyId();
        Company company = companyMapper.selectById(companyId);
        if ("1".equals(company.getIsFreeze())) {
            throw new XkdException("账户已被冻结");
        }
        if (!active.equals("prd"))
            throw new XkdException("测试环境不允许操作");
        CompanyAccount companyAccount = companyAccountMapper.selectById(companyId);
        if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
            if (ObjectUtil.isNotEmpty(companyAccount) && companyAccount.getPrestoreMemberId().isEmpty()) {
                throw new XkdException("请绑定线下分账商户号");
            }
        }
        String phone = checkOutCode(outInfoDto, companyId);
        RLock lock = redissonClient.getLock(transferOrderLock + companyId);
        String lockName = SysConstant.FEE_LOCK + companyId;
        RLock lock2 = redissonClient.getLock(lockName);
        try {
            log.info("尝试获取锁: {}", lockName);
            lock2.lock();
            log.info("成功获取锁: {}", lockName);
            //判断服务费是为负
            BigDecimal bigDecimal = companyMapper.selectBalance(companyId);
            if (bigDecimal.compareTo(BigDecimal.ZERO) < 0)
                throw new XkdException("服务费不足，无法放款");
        } catch (Exception e) {
            throw new XkdException(e.getMessage());
        } finally {
            if (lock2.isHeldByCurrentThread()) {
                lock2.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), lockName);
            }
        }
        try {
            log.info("尝试获取锁: {}", transferOrderLock + companyId);
            lock.lock();
            log.info("成功获取锁: {}", transferOrderLock + companyId);
            //查询放款账户余额是否充足
            List<String> collect = Arrays.stream(company.getThoroughFare().split(",")).map(String::valueOf).collect(Collectors.toList());
            if (!collect.contains(outInfoDto.getPayPass())) {
                throw new XkdException("通道类型错误");
            }
            long oa = 0;
            if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
                oa = company.getOutBalance() - company.getOutBalanceFreeze();
            } else if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {
                oa = company.getTowBaofuBalance() - company.getTowOutBalanceFreeze();
            }
            //查询是否绑卡
            String cardStatus = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, outInfoDto.getIncomingId())).getCardStatus();
            if ("0".equals(cardStatus)) {
                throw new XkdException("还款人未绑定银行卡");
            }
            if (outInfoDto.getLoanAmount().compareTo(new BigDecimal(oa).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)) > 0)
                throw new XkdException("放款账户余额不足");
            if (ObjectUtil.isNotEmpty(outInfo) && !(SysConstant.OUT_INFO_PAY_FOUR.equals(outInfo.getOutStatus()) || SysConstant.OUT_INFO_PAY_FIVE.equals(outInfo.getOutStatus()))) {
                throw new XkdException("放款状态异常,不允许重新放款");
            }
            BigDecimal loanAmount = outInfoDto.getLoanAmount();
            BigDecimal outLoan = outInfo.getOutLoan();
            //判断是否超放
            if (loanAmount.compareTo(outLoan) > 0) {
                throw new XkdException("放款金额大于申请提款金额");
            }
            Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId));
            BeanUtil.copyProperties(outInfoDto, outInfo);
            outInfo.setOutLoan(loanAmount);
            outInfo.setOutStatus(SysConstant.OUT_INFO_PAY_TOW);
            updateById(outInfo);

            OutAmountDto outAmountDto = new OutAmountDto();
            outAmountDto.setCompanyId(credit.getCompanyId());
            outAmountDto.setToBankName(outInfoDto.getBank());
            outAmountDto.setToAccName(outInfoDto.getToAccName());
            outAmountDto.setToAccNo(outInfoDto.getToAccNo());
            outAmountDto.setId(outInfo.getOutId());
            outAmountDto.setOutId(outId);
            outAmountDto.setTransCardId(outInfoDto.getTransCardId());
            outAmountDto.setIncomingId(incomingId);
            outAmountDto.setTransMoney(loanAmount + "");
            outAmountDto.setChannelType(outInfoDto.getPayPass());
            outAmountDto.setTransCnap(outInfoDto.getToBankCode());
            //插入订单表
            String order = OrderNumUtil.getOutAmountOrder();
            long i = new BigDecimal(outAmountDto.getTransMoney()).multiply(new BigDecimal(100)).intValue();

            if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
                OutOrderInfo outOrderInfo = new OutOrderInfo();
                outOrderInfo.setOrderId(outInfo.getOutId());
                outOrderInfo.setOrderNo(order);
                outOrderInfo.setAmount(i);
                outOrderInfo.setCompanyId(companyId);
                outOrderInfo.setIncomingId(incomingId);
                outOrderInfo.setToAccName(outAmountDto.getToAccName());
                outOrderInfo.setToAccNo(outAmountDto.getToAccNo());
                outOrderInfo.setOutTime(new Date());
                outOrderInfo.setOutId(outId);
                outOrderInfo.setTransCardId(outInfoDto.getToBankCode());
                outOrderInfo.setTransCnap(outInfoDto.getToBankCode());
                outOrderInfo.setToBankName(outAmountDto.getToBankName());
                outOrderInfo.setTransCardId(outAmountDto.getTransCardId());
                outOrderInfoMapper.insert(outOrderInfo);
            } else if (outInfoDto.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {
                ChannelPayTow channelPayTow = new ChannelPayTow();
                channelPayTow.setOrderNo(order);
                channelPayTow.setPayType("1");
                channelPayTow.setAmount(i);
                channelPayTow.setAccountType("1");
                channelPayTow.setPayStatus("1");
                channelPayTow.setIncomingId(incomingId);
                channelPayTow.setOutId(outId);
                channelPayTow.setCompanyId(companyId);
                channelPayTow.setCreateTime(new Date());
                channelPayTow.setToAccName(outInfoDto.getToAccName());
                channelPayTow.setToAccNo(outInfoDto.getToAccNo());
                channelPayTow.setToBankName(outInfoDto.getBank());
                channelPayTow.setTransCardId(outInfoDto.getTransCardId());
                channelPayTowMapper.insert(channelPayTow);
            }
            outAmountDto.setOrder(order);
            // 确保锁的释放在事务提交之后
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {//事务成功后执行
                    try {
                        dlApiService.outAmount(outAmountDto);
                    } catch (Exception e) {
                        throw new XkdException("下一期扣款错误");
                    }
                }
            });
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), transferOrderLock + companyId);
            }
            //放款后删除验证码
            redisTemplate.delete("out_amount:" + outInfoDto.getOutTimestamp() + phone);
        }

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String goLending(IncomingIdVo incomingIdVo) {
        Integer incomingId = incomingIdVo.getIncomingId();

        Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId));
        OutInfo outInfo = outInfoMapper.selectOne(new LambdaQueryWrapper<OutInfo>().eq(OutInfo::getIncomingId, incomingId));
        if (ObjectUtil.isEmpty(outInfo)) {
            throw new XkdException("用户业务状态异常");
        }
        IncomingInfo info = incomingInfoMapper.selectById(incomingId);
        if (SysConstant.ORDER_DISABLE.equals(info.getIsEnable())) {
            throw new XkdException("该订单已被平台停用");
        }
        String isWithhold = incomingIdVo.getIsWithhold();
        if (StringUtils.isEmpty(isWithhold)) {
            throw new XkdException("代扣方式未填");
        }

        //校验合同是否签署完毕
        Integer count = contractMapper.selectCount(new LambdaQueryWrapper<Contract>()
                .eq(Contract::getOutId, outInfo.getOutId())
                .eq(Contract::getType, SysConstant.CONTRACT_TYPE_TOW)
                .eq(Contract::getStatus, SysConstant.CONTRACT_DO));
        if (count != 1) {
            throw new XkdException("用户合同未签署完毕");
        }
        //校验是否第一次放款
        if (outInfo.getOutStatus().equals(SysConstant.OUT_INFO_PAY_TOW) || outInfo.getOutStatus().equals(SysConstant.OUT_INFO_PAY_THREE)) {
            throw new XkdException("用户放款状态异常");
        }
        Integer companyId = credit.getCompanyId();
        CompanyAccount companyAccount = companyAccountMapper.selectById(companyId);
        if (outInfo.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {//通道一验证
            if (ObjectUtil.isNotEmpty(companyAccount) && companyAccount.getPrestoreMemberId().isEmpty()) {
                throw new XkdException("请绑定线下分账商户号");
            }
        }
        RLock lock = redissonClient.getLock(transferOrderLock + companyId);
        String lockName = SysConstant.FEE_LOCK + companyId;
        RLock lock2 = redissonClient.getLock(lockName);
        Company company = null;
        try {
            log.info("尝试获取锁: {}", lockName);
            lock2.lock();
            log.info("成功获取锁: {}", lockName);
            company = companyMapper.selectById(companyId);

            //如果公司是 不代扣，放款只能选择 不代扣
            if (company.getWithholdSwitch().equals("1") && isWithhold.equals("0")) {
                throw new XkdException("当前公司还款方式为不代扣记账，如需调整请联系平台管理员");
            }
            //判断服务费是为负
            String accountingType = company.getAccountingType();
            BigDecimal balance;
            if (!SysConstant.ACCOUNTING_TYPE_ONE.equals(accountingType)) {
                //集团服务费余额
                balance = companyMapper.selectOne(new LambdaQueryWrapper<Company>().eq(Company::getId, company.getBlocId())).getBalance();
            } else {
                //资方服务费余额
                balance = company.getBalance();
            }
            if (balance.compareTo(BigDecimal.ZERO) < 0)
                throw new XkdException("服务费不足，无法放款");
        } catch (Exception e) {
            throw new XkdException(e.getMessage());
        } finally {
            if (lock2.isHeldByCurrentThread()) {
                lock2.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), lockName);
            }
        }
        String order = null;
        try {
            log.info("尝试获取锁: {}", transferOrderLock + companyId);
            lock.lock();
            log.info("成功获取锁: {}", transferOrderLock + companyId);
            //查询是否绑卡
            String cardStatus = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId)).getCardStatus();
            if ("0".equals(cardStatus)) {
                throw new XkdException("还款人未绑定银行卡");
            }
            //查询放款账户余额是否充足
            long oa = 0;//剩余余额
            if (outInfo.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
                oa = company.getOutBalance() - company.getOutBalanceFreeze();
            } else if (outInfo.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {
                oa = company.getTowBaofuBalance() - company.getTowOutBalanceFreeze();
            }
            if (credit.getLoanAmount().compareTo(new BigDecimal(oa).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)) > 0)
                throw new XkdException("放款账户余额不足");

            BigDecimal loanAmount = outInfo.getLoanAmount();
            outInfo.setOutStatus(SysConstant.OUT_INFO_PAY_TOW);
            outInfo.setStatus(SysConstant.BUSINESS_STATUS_TWELVE);
            Calendar loanStartCalendar = Calendar.getInstance();
            Calendar loanStartCalendar1 = Calendar.getInstance();
            Date date = new Date();
            Integer term = outInfo.getTerm();
            String repayDay = outInfo.getRepayDay();
            //按月
            if ("1".equals(outInfo.getRepayFrequency())) {
                loanStartCalendar1.add(Calendar.MONTH, 1);
                loanStartCalendar1.set(Calendar.DAY_OF_MONTH, Integer.parseInt(repayDay));

                loanStartCalendar.add(Calendar.MONTH, term);
                loanStartCalendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(repayDay));
                // 如果还款日是1号，并且今天是月底（比如30或31号），则多推一个月
                if ("1".equals(repayDay)) {
                    Calendar today = Calendar.getInstance();
                    int currentDay = today.get(Calendar.DAY_OF_MONTH);
                    int lastDayOfMonth = today.getActualMaximum(Calendar.DAY_OF_MONTH);
                    if (currentDay == lastDayOfMonth) {
                        loanStartCalendar.add(Calendar.MONTH, 1); // 多推一个月
                    }
                }
                outInfo.setOutEndTime(loanStartCalendar.getTime());

            } else if ("2".equals(outInfo.getRepayFrequency())) {
                int termUse = outInfo.getTerm(); // 总贷款周数
                int repayDayNum = Integer.parseInt(repayDay); // 用户输入还款日（1-7：周一至周日）
                int repayDayCalendar; // 转换为Calendar.DAY_OF_WEEK（1=周日,2=周一,...,7=周六）

                // 修正：支持周六（6→7）和周日（7→1）的Calendar转换
                if (repayDayNum == 7) {
                    // 周日特殊处理：用户输入7 → Calendar.SUNDAY=1
                    repayDayCalendar = Calendar.SUNDAY;
                } else {
                    // 周一至周六：用户输入1-6 → 2-7（Calendar.MONDAY=2至SATURDAY=7）
                    repayDayCalendar = repayDayNum + 1;
                }

                // 获取当前日期的周几（1=周日,2=周一,...,7=周六）
                Calendar today = Calendar.getInstance();
                int currentDayOfWeek = today.get(Calendar.DAY_OF_WEEK);

                // 今天不是还款日时，结束日期减少1周（含周末还款日场景）
                if (currentDayOfWeek != repayDayCalendar) {
                    termUse -= 1;
                }

                // 计算结束日期（原有逻辑保持，支持周末还款日）
                Calendar loanEndCalendar = Calendar.getInstance();
                loanEndCalendar.add(Calendar.WEEK_OF_MONTH, termUse);
                int endDayOfWeek = loanEndCalendar.get(Calendar.DAY_OF_WEEK);
                int endDaysAdjust = (repayDayCalendar - endDayOfWeek + 7) % 7;
                loanEndCalendar.add(Calendar.DAY_OF_YEAR, endDaysAdjust);
                outInfo.setOutEndTime(loanEndCalendar.getTime());

                // 修正最近还款日计算（支持周末还款日）
                Calendar latestRepayCalendar = Calendar.getInstance();
                int daysToNextRepay = (repayDayCalendar - currentDayOfWeek + 7) % 7;
                // 若今天是还款日（含周六/周日），则顺延至下一周
                if (daysToNextRepay == 0) {
                    daysToNextRepay = 7;
                }
                latestRepayCalendar.add(Calendar.DAY_OF_YEAR, daysToNextRepay);

            } else {
                //按日
                loanStartCalendar.add(Calendar.DAY_OF_MONTH, outInfo.getTerm());
                outInfo.setOutEndTime(loanStartCalendar.getTime());
            }
            outInfo.setOutStartTime(date);
            updateById(outInfo);
            //插入订单表
            order = OrderNumUtil.getOutAmountOrder();
            long i = loanAmount.multiply(new BigDecimal(100)).intValue();
            ChannelPayTow channelPayTow = new ChannelPayTow();
            channelPayTow.setOrderNo(order);
            channelPayTow.setPayType("1");
            channelPayTow.setAmount(i);
            channelPayTow.setAccountType("1");
            channelPayTow.setPayStatus("1");
            channelPayTow.setIncomingId(incomingId);
            channelPayTow.setOutId(outInfo.getOutId());
            channelPayTow.setCompanyId(companyId);
            channelPayTow.setCreateTime(new Date());
            channelPayTow.setToAccName(credit.getAccountName());
            channelPayTow.setToAccNo(credit.getReceivingAccount());
            channelPayTow.setToBankName(credit.getBank());
            channelPayTow.setTransCardId(info.getIdCard());
            channelPayTowMapper.insert(channelPayTow);

            OutAmountDto outAmountDto = new OutAmountDto();
            outAmountDto.setCompanyId(credit.getCompanyId());
            outAmountDto.setToBankName(credit.getBank());
            outAmountDto.setToAccName(credit.getAccountName());
            outAmountDto.setToAccNo(credit.getReceivingAccount());
            outAmountDto.setId(outInfo.getOutId());
            outAmountDto.setOutId(outInfo.getOutId());
            outAmountDto.setTransCardId(info.getIdCard());
            outAmountDto.setIncomingId(incomingId);
            outAmountDto.setTransMoney(loanAmount + "");
            outAmountDto.setChannelType(outInfo.getPayPass());
            outAmountDto.setOrder(order);
            // 确保锁的释放在事务提交之后
            String finalOrder = order;
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {//事务成功后执行
                    try {
                        if (active.equals("prd")) {
                            dlApiService.outAmount(outAmountDto);
                        } else {
                            testPassTowOutAmount(incomingId, outInfo.getOutId(), companyId, i, finalOrder);
                        }
                    } catch (Exception e) {
                        log.error("放款异常：{}，异常信息：{}", e.getMessage(), e);
                        throw new XkdException("放款失败");
                    }
                }
            });
        } catch (Exception e) {
            log.error("放款异常：{},异常信息: {}", e.getMessage(), e);
            throw new XkdException(e.getMessage());
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), transferOrderLock + companyId);
            }
        }
        redisTemplate.opsForValue().set(outInfo.getOutId() + "", isWithhold, 1, TimeUnit.DAYS);
        return order;
    }


    @Override
    public OutDetailVo lendingDetail(Integer incomingId) {
        return outInfoMapper.selectOutDetail(incomingId);
    }

    @Override
    public Page<OutInfoPageAppVo> outInfoAppList(OutInfoAppQuery query) {
        String companyType = SecurityUtil.getLoginUser().getUser().getCompanyType();
        Integer companyId = query.getCompanyId();
        List<Integer> userIds = null;
        if (!companyType.equals(SysConstant.USER_COMPANY_TYPE_ONE) && !companyType.equals(SysConstant.USER_COMPANY_TYPE_FOUR)) {//不是平台和集团
            companyId = SecurityUtil.getLoginCompanyId();
            userIds = iSysUserService.getUserIdsByGroup();
        }
        PageHelper.startPage(query.getPageNum(), query.getPageSize());
        return (Page<OutInfoPageAppVo>) outInfoMapper.outInfoAppList(query, userIds, companyId, companyType);
    }

    @Override
    public OutInfoAppVo outInfoAppDetail(Integer incomingId) {
        OutInfoAppVo outInfoAppVo = outInfoMapper.outInfoAppDetail(incomingId);
        String outAmount = outInfoAppVo.getOutAmount();
        String s = outAmount + " " + XkdUtil.number2CNMontrayUnit(new BigDecimal(outAmount));
        outInfoAppVo.setOutAmount(s);
        List<RepaymentSchedule> repaymentSchedules = repaymentScheduleMapper.selectList(new LambdaQueryWrapper<RepaymentSchedule>().eq(RepaymentSchedule::getIncomingId, incomingId));
        if (CollectionUtil.isNotEmpty(repaymentSchedules)) {
            Date repaymentDate = repaymentSchedules.get(repaymentSchedules.size() - 1).getRepaymentDate();
            outInfoAppVo.setOutEndTime(repaymentDate);
        }
        return outInfoAppVo;
    }

    @Override
    public void exportOutInfoAppList(OutInfoAppQuery query, Integer id) {
        String name = "放款列表外接app-excel文件" + UUID.randomUUID().toString().replace("-", "");
        Integer companyId = SecurityUtil.getLoginCompanyId();
        FileOutputStream fileOutputStream = null;
        MissionCenterInfo missionCenterInfo = new MissionCenterInfo();
        missionCenterInfo.setId(id);
        missionCenterInfo.setFileName(name + ".xlsx");

        String filePath = localPathDir + exportFile + "/" + companyId;
        File dirFile = new File(filePath);
        if (!dirFile.exists()) {
            dirFile.mkdirs();
        }

        try {
            List<RowHeightColWidthModel> rowHeightColWidthList = new ArrayList<>();
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 0, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 1, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 2, 10));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 3, 15));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 4, 10));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 5, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 6, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 7, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 8, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 9, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 10, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 11, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 12, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 13, 20));
            rowHeightColWidthList.add(RowHeightColWidthModel.createColWidthModel(name, 14, 20));
            File file = new File(filePath + "/" + name + ".xlsx");
            fileOutputStream = new FileOutputStream(file);

            ExcelWriter write = EasyExcel.write(fileOutputStream, OutInfoPageAppVo.class).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(name).registerWriteHandler(new CustomRowHeightColWidthHandler(rowHeightColWidthList)).build();
            query.setPageSize(0);
            List<OutInfoPageAppVo> list = outInfoAppList(query);
            for (OutInfoPageAppVo customerInfoVo : list) {
                String decryptPhone = AESUtil.decrypt(customerInfoVo.getPhone(), aesKey);
                customerInfoVo.setPhone(decryptPhone);
            }
            write.write(list, writeSheet);
            write.finish();
            fileOutputStream.flush();
            //修改文件状态 导出成功
            missionCenterInfo.setFileStatus(SysConstant.YSZT_FILE_STATUS_TOW);
            missionCenterInfo.setFileUrl(exportFile + "/" + companyId + "/" + name + ".xlsx");
            missionCenterInfoMapper.updateById(missionCenterInfo);
        } catch (Exception e) {
            //修改文件状态 导出失败
            missionCenterInfo.setFileStatus(SysConstant.YSZT_FILE_STATUS_THREE);
            missionCenterInfoMapper.updateById(missionCenterInfo);
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) {
        Calendar calendar = Calendar.getInstance();
        int daysToTarget = (7 - 1 + 8) % 7;
        System.out.println(daysToTarget);
        calendar.add(Calendar.WEEK_OF_MONTH, 1);
        calendar.add(Calendar.DAY_OF_YEAR, daysToTarget);
        System.out.println(new Date(calendar.getTimeInMillis()));
    }


    /**
     * 计算贷款还款计划
     *
     * @param outInfo 放款信息
     */
    @Override
    public List<RepaymentSchedule> calculateLoanSchedule(OutInfo outInfo) {
        List<RepaymentSchedule> schedule = new ArrayList<>();
        String repayFrequency = outInfo.getRepayFrequency();
        BigDecimal annualRate = outInfo.getRate();
        BigDecimal loanAmount = outInfo.getLoanAmount();
        Integer companyId = outInfo.getCompanyId();
        Integer loanTerm = outInfo.getTerm();
        String repayDay = outInfo.getRepayDay();
        String repayType = outInfo.getLoanMethod();
        int repayDay1 = 0;

        // 计算利率
        BigDecimal dayRate = annualRate.divide(BigDecimal.valueOf(36000), 10, RoundingMode.HALF_UP);// 按天利率 = 年利率 / 360
        BigDecimal monthlyRate = annualRate.divide(BigDecimal.valueOf(1200), 10, RoundingMode.HALF_UP); // 按月利率 = 年利率 / 12
        BigDecimal weekRate = annualRate.divide(BigDecimal.valueOf(5200), 10, RoundingMode.HALF_UP); // 按月利率 = 年利率 / 52

        BigDecimal remainingPrincipal = loanAmount; // 剩余本金
        BigDecimal totalInterest = BigDecimal.ZERO;

        // 累计利息
        BigDecimal totalInterestAcc = BigDecimal.ZERO;

        BigDecimal term = new BigDecimal(loanTerm);

        // 获取当前日期
        Calendar calendar = Calendar.getInstance();
        Date currentDate = new Date(calendar.getTimeInMillis());
        Integer incomingId = outInfo.getIncomingId();

        // 计算首次还款日期
        if (SysConstant.REPAY_FREQUENCY_MONTH.equals(repayFrequency) || StringUtils.isBlank(repayFrequency)) {
            // 按月还款
            calendar.setTime(currentDate);
            repayDay1 = Integer.parseInt(repayDay);
            calendar.add(Calendar.MONTH, 1);
            calendar.set(Calendar.DAY_OF_MONTH, repayDay1);
        } else if (SysConstant.REPAY_FREQUENCY_WEEK.equals(repayFrequency)) {
            calendar.setTime(currentDate);
            int currentDayOfWeek1 = calendar.get(Calendar.DAY_OF_WEEK);
            repayDay1 = Integer.parseInt(repayDay);
            //如果当前日期等于还款日期，还款日期变成下一周
            if (currentDayOfWeek1 == repayDay1 + 1 || currentDayOfWeek1 == Calendar.SUNDAY) {
                calendar.add(Calendar.WEEK_OF_MONTH, 1);
            }
            int daysToTarget = (Integer.parseInt(repayDay) - currentDayOfWeek1 + 8) % 7;
            calendar.add(Calendar.DAY_OF_YEAR, daysToTarget);
        }
        switch (repayType) {
            // 等额本金
            case SysConstant.REPAY_TYPE_ZERO: {
                if (SysConstant.REPAY_FREQUENCY_WEEK.equals(repayFrequency)) {//按周
                    // 每周应还本金 = 贷款本金 / 总还款周数
                    BigDecimal weeklyPrincipal = loanAmount.divide(BigDecimal.valueOf(loanTerm), 2, RoundingMode.HALF_UP);
                    int actualDays;
                    Calendar loanStartCalendar = Calendar.getInstance();
                    loanStartCalendar.setTime(new Date());
                    int currentDayOfWeek = loanStartCalendar.get(Calendar.DAY_OF_WEEK) - 1;
                    //如果当前日期大于等于还款日期
                    if (currentDayOfWeek >= repayDay1) {
                        //放款日到周末的实际天数
                        actualDays = (Calendar.SATURDAY - currentDayOfWeek + 7) % 7 + 1;
                        //首周+实际放款日
                        actualDays = actualDays + repayDay1;
                    } else if (currentDayOfWeek == 0) {
                        actualDays = 7;
                    } else {
                        actualDays = repayDay1 - currentDayOfWeek + 1;
                    }
                    // 首周应还利息 = 剩余本金 × 日利率 × 实际借款天数
                    BigDecimal firstWeekInterest = remainingPrincipal.multiply(dayRate)
                            .multiply(BigDecimal.valueOf(actualDays))
                            .setScale(2, RoundingMode.HALF_UP);
                    //累计利息
                    totalInterestAcc = totalInterestAcc.add(firstWeekInterest);
                    totalInterest = totalInterestAcc;
                    // 首周应还总额 = 每周应还本金 + 首周应还利息
                    BigDecimal firstWeekPayment = weeklyPrincipal.add(firstWeekInterest);

                    // 更新剩余本金
                    remainingPrincipal = remainingPrincipal.subtract(weeklyPrincipal);

                    // 设置首周还款日期
                    Date firstRepaymentDate = new Date(calendar.getTimeInMillis());

                    RepaymentSchedule firstPayment = new RepaymentSchedule();
                    firstPayment.setOutId(outInfo.getOutId());
                    firstPayment.setAmount(firstWeekPayment);
                    firstPayment.setPrincipal(weeklyPrincipal);
                    firstPayment.setInterest(firstWeekInterest);
                    firstPayment.setInstallmentNumber(1);
                    firstPayment.setRepaymentDate(firstRepaymentDate);
                    firstPayment.setCreateTime(currentDate);
                    firstPayment.setPrincipalTodo(weeklyPrincipal);
                    firstPayment.setInterestTodo(firstWeekInterest);
                    firstPayment.setIncomingId(incomingId);
                    schedule.add(firstPayment);

                    // 按周还款，增加一个周
                    calendar.add(Calendar.WEEK_OF_MONTH, 1);
                    // 次周及以后的计算
                    for (int period = 2; period <= loanTerm; period++) {
                        // 每周应还利息 = 剩余本金 × 月利率
                        BigDecimal weekInterest = remainingPrincipal.multiply(weekRate).setScale(2, RoundingMode.HALF_UP);

                        // 每月应还总额 = 每月应还本金 + 每月应还利息
                        BigDecimal monthlyPayment = weeklyPrincipal.add(weekInterest);
                        totalInterestAcc = totalInterestAcc.add(weekInterest);
                        totalInterest = totalInterestAcc;
                        // 更新剩余本金
                        remainingPrincipal = remainingPrincipal.subtract(weeklyPrincipal);

                        // 设置还款日期
                        Date repaymentDate = new Date(calendar.getTimeInMillis());

                        RepaymentSchedule payment = new RepaymentSchedule();
                        payment.setOutId(outInfo.getOutId());
                        payment.setAmount(monthlyPayment);
                        payment.setPrincipal(weeklyPrincipal);
                        payment.setInterest(weekInterest);
                        payment.setInstallmentNumber(period);
                        payment.setRepaymentDate(repaymentDate);
                        payment.setCreateTime(currentDate);
                        payment.setPrincipalTodo(weeklyPrincipal);
                        payment.setInterestTodo(weekInterest);
                        payment.setIncomingId(incomingId);
                        schedule.add(payment);
                        // 按月还款，增加一个月
                        calendar.add(Calendar.WEEK_OF_MONTH, 1);
                    }

                } else {//按月
                    // 每月应还本金 = 贷款本金 / 总还款月数
                    BigDecimal monthlyPrincipal = loanAmount.divide(BigDecimal.valueOf(loanTerm), 2, RoundingMode.HALF_UP);

                    // 计算首月实际借款天数
                    Calendar loanStartCalendar = Calendar.getInstance();
                    loanStartCalendar.setTime(new Date());

                    int actualDays;

                    // 如果放款日是1号，则实际借款天数为30天
                    if (loanStartCalendar.get(Calendar.DAY_OF_MONTH) == 1) {
                        actualDays = 30; // 固定为30天
                    } else {
                        // 否则，计算从放款日到月底的实际天数
                        actualDays = loanStartCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) - loanStartCalendar.get(Calendar.DAY_OF_MONTH) + 1;
                        actualDays += repayDay1; // 加上还款日
                    }

                    // 首月应还利息 = 剩余本金 × 日利率 × 实际借款天数
                    BigDecimal firstMonthInterest = remainingPrincipal.multiply(dayRate)
                            .multiply(BigDecimal.valueOf(actualDays))
                            .setScale(2, RoundingMode.HALF_UP);
                    //累计利息
                    totalInterestAcc = totalInterestAcc.add(firstMonthInterest);
                    totalInterest = totalInterestAcc;
                    // 首月应还总额 = 每月应还本金 + 首月应还利息
                    BigDecimal firstMonthPayment = monthlyPrincipal.add(firstMonthInterest);

                    // 更新剩余本金
                    remainingPrincipal = remainingPrincipal.subtract(monthlyPrincipal);

                    // 设置首月还款日期
                    Date firstRepaymentDate = new Date(calendar.getTimeInMillis());

                    RepaymentSchedule firstPayment = new RepaymentSchedule();
                    firstPayment.setOutId(outInfo.getOutId());
                    firstPayment.setAmount(firstMonthPayment);
                    firstPayment.setPrincipal(monthlyPrincipal);
                    firstPayment.setInterest(firstMonthInterest);
                    firstPayment.setInstallmentNumber(1);
                    firstPayment.setRepaymentDate(firstRepaymentDate);
                    firstPayment.setCreateTime(currentDate);
                    firstPayment.setPrincipalTodo(monthlyPrincipal);
                    firstPayment.setInterestTodo(firstMonthInterest);
                    firstPayment.setIncomingId(incomingId);
                    schedule.add(firstPayment);

                    // 按月还款，增加一个月
                    calendar.add(Calendar.MONTH, 1);
                    calendar.set(Calendar.DAY_OF_MONTH, repayDay1);

                    // 次月及以后的计算
                    for (int period = 2; period <= loanTerm; period++) {
                        // 每月应还利息 = 剩余本金 × 月利率
                        BigDecimal monthlyInterest = remainingPrincipal.multiply(monthlyRate).setScale(2, RoundingMode.HALF_UP);

                        // 每月应还总额 = 每月应还本金 + 每月应还利息
                        BigDecimal monthlyPayment = monthlyPrincipal.add(monthlyInterest);
                        totalInterestAcc = totalInterestAcc.add(monthlyInterest);
                        totalInterest = totalInterestAcc;
                        // 更新剩余本金
                        remainingPrincipal = remainingPrincipal.subtract(monthlyPrincipal);

                        // 设置还款日期
                        Date repaymentDate = new Date(calendar.getTimeInMillis());

                        RepaymentSchedule payment = new RepaymentSchedule();
                        payment.setOutId(outInfo.getOutId());
                        payment.setAmount(monthlyPayment);
                        payment.setPrincipal(monthlyPrincipal);
                        payment.setInterest(monthlyInterest);
                        payment.setInstallmentNumber(period);
                        payment.setRepaymentDate(repaymentDate);
                        payment.setCreateTime(currentDate);
                        payment.setPrincipalTodo(monthlyPrincipal);
                        payment.setInterestTodo(monthlyInterest);
                        payment.setIncomingId(incomingId);
                        schedule.add(payment);

                        // 按月还款，增加一个月
                        calendar.add(Calendar.MONTH, 1);
                        calendar.set(Calendar.DAY_OF_MONTH, repayDay1);
                    }

                }
                break;
            }
            // 等额本息
            case SysConstant.REPAY_TYPE_ONE: {
                if (SysConstant.REPAY_FREQUENCY_WEEK.equals(repayFrequency)) {//按周
                    // 每周还款额 X = P * [r(1+r)^n] / [(1+r)^n - 1]
                    BigDecimal numerator = weekRate.multiply(BigDecimal.ONE.add(weekRate).pow(loanTerm));
                    BigDecimal denominator = BigDecimal.ONE.add(weekRate).pow(loanTerm).subtract(BigDecimal.ONE);
                    BigDecimal weeklyPayment = loanAmount.multiply(numerator).divide(denominator, 2, RoundingMode.HALF_UP);
                    // 总还款额 = 每月还款额 * 总还款月数
                    BigDecimal totalRepayment = weeklyPayment.multiply(BigDecimal.valueOf(loanTerm));
                    // 总利息 = 总还款额 - 贷款本金
                    totalInterest = totalRepayment.subtract(loanAmount);
                    //剩余利息
                    BigDecimal remainingInterest = totalInterest;
                    int actualDays;
                    Calendar loanStartCalendar = Calendar.getInstance();
                    loanStartCalendar.setTime(new Date());
                    int currentDayOfWeek = loanStartCalendar.get(Calendar.DAY_OF_WEEK) - 1;
                    System.out.println(currentDayOfWeek);
                    //如果当前日期大于等于还款日期
                    if (currentDayOfWeek >= repayDay1) {
                        //放款日到周末的实际天数
                        actualDays = (Calendar.SATURDAY - currentDayOfWeek + 7) % 7 + 1;
                        //首周+实际放款日
                        actualDays = actualDays + repayDay1;
                    } else if (currentDayOfWeek == 0) {
                        actualDays = 7;
                    } else {
                        actualDays = repayDay1 - currentDayOfWeek + 1;
                    }

                    // 首月应还利息 = 剩余本金 * 日利率 * 实际借款天数
                    BigDecimal firstWeekInterest = remainingPrincipal.multiply(dayRate)
                            .multiply(BigDecimal.valueOf(actualDays))
                            .setScale(2, RoundingMode.HALF_UP);
                    //累计利息
                    totalInterestAcc = totalInterestAcc.add(firstWeekInterest);
                    //剩余利息
                    remainingInterest = remainingInterest.subtract(firstWeekInterest);
                    // 首月应还本金 = 每月还款额 - 首月应还利息
                    BigDecimal firstWeekPrincipal = weeklyPayment.subtract(firstWeekInterest);
                    // 更新剩余本金
                    remainingPrincipal = remainingPrincipal.subtract(firstWeekPrincipal);
                    // 设置首周还款日期
                    Date firstRepaymentDate = new Date(calendar.getTimeInMillis());
                    RepaymentSchedule firstPayment = new RepaymentSchedule();
                    firstPayment.setOutId(outInfo.getOutId());
                    firstPayment.setAmount(weeklyPayment);
                    firstPayment.setPrincipal(firstWeekPrincipal);
                    firstPayment.setInterest(firstWeekInterest);
                    firstPayment.setInstallmentNumber(1);
                    firstPayment.setRepaymentDate(firstRepaymentDate);
                    firstPayment.setCreateTime(currentDate);
                    firstPayment.setPrincipalTodo(firstWeekPrincipal);
                    firstPayment.setInterestTodo(firstWeekInterest);
                    firstPayment.setIncomingId(incomingId);
                    schedule.add(firstPayment);
                    // 按周还款，增加一周
                    calendar.add(Calendar.WEEK_OF_MONTH, 1);
                    // 次周及以后的计算
                    for (int period = 2; period <= loanTerm - 1; period++) {
                        // 每周应还利息 = 剩余本金 * 周利率
                        BigDecimal weeklyInterest = remainingPrincipal.multiply(weekRate).setScale(2, RoundingMode.HALF_UP);

                        // 每周应还本金 = 每周还款额 - 每周应还利息
                        BigDecimal weeklyPrincipal = weeklyPayment.subtract(weeklyInterest);
                        //如果剩余利息小于等于每月应还利息，则本月应还利息等于剩余利息
                        if (remainingInterest.compareTo(weeklyInterest) <= 0) {
                            weeklyInterest = remainingInterest;
                            weeklyPrincipal = weeklyPayment.subtract(weeklyInterest);
                        }
                        //累计利息
                        totalInterestAcc = totalInterestAcc.add(weeklyInterest);
                        // 更新剩余本金
                        remainingPrincipal = remainingPrincipal.subtract(weeklyPrincipal);

                        // 设置还款日期
                        Date repaymentDate = new Date(calendar.getTimeInMillis());

                        RepaymentSchedule payment = new RepaymentSchedule();
                        remainingInterest = remainingInterest.subtract(weeklyInterest);
                        //
                        payment.setOutId(outInfo.getOutId());
                        payment.setAmount(weeklyPayment);
                        payment.setPrincipal(weeklyPrincipal);
                        payment.setInterest(weeklyInterest);
                        payment.setInstallmentNumber(period);
                        payment.setRepaymentDate(repaymentDate);
                        payment.setCreateTime(currentDate);
                        payment.setPrincipalTodo(weeklyPrincipal);
                        payment.setInterestTodo(weeklyInterest);
                        payment.setIncomingId(incomingId);
                        schedule.add(payment);

                        // 按周还款，增加一周
                        calendar.add(Calendar.WEEK_OF_MONTH, 1);
                    }

                    BigDecimal lastMonth = totalInterest.subtract(totalInterestAcc);
                    BigDecimal amount = lastMonth.add(remainingPrincipal);
                    Date repaymentDate = new Date(calendar.getTimeInMillis());
                    RepaymentSchedule payment = new RepaymentSchedule();
                    payment.setOutId(outInfo.getOutId());
                    payment.setAmount(amount);
                    payment.setPrincipal(remainingPrincipal);
                    payment.setInterest(lastMonth);
                    payment.setInstallmentNumber(loanTerm);
                    payment.setRepaymentDate(repaymentDate);
                    payment.setCreateTime(currentDate);
                    payment.setPrincipalTodo(remainingPrincipal);
                    payment.setInterestTodo(lastMonth);
                    payment.setIncomingId(incomingId);
                    schedule.add(payment);
                } else {//按月
                    // 每月还款额 X = P * [r(1+r)^n] / [(1+r)^n - 1]
                    BigDecimal numerator = monthlyRate.multiply(BigDecimal.ONE.add(monthlyRate).pow(loanTerm));
                    BigDecimal denominator = BigDecimal.ONE.add(monthlyRate).pow(loanTerm).subtract(BigDecimal.ONE);
                    BigDecimal monthlyPayment = loanAmount.multiply(numerator).divide(denominator, 2, RoundingMode.HALF_UP);

                    // 总还款额 = 每月还款额 * 总还款月数
                    BigDecimal totalRepayment = monthlyPayment.multiply(BigDecimal.valueOf(loanTerm));

                    // 总利息 = 总还款额 - 贷款本金
                    totalInterest = totalRepayment.subtract(loanAmount);
                    //剩余利息
                    BigDecimal remainingInterest = totalInterest;

                    // 计算首月实际借款天数
                    Calendar loanStartCalendar = Calendar.getInstance();
                    loanStartCalendar.setTime(new Date()); // loanStartDate 是放款日期

                    int actualDays;

                    // 如果放款日是1号，则实际借款天数为30天
                    if (loanStartCalendar.get(Calendar.DAY_OF_MONTH) == 1) {
                        actualDays = 30; // 固定为30天
                    } else {
                        // 否则，计算从放款日到月底的实际天数
                        actualDays = loanStartCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) - loanStartCalendar.get(Calendar.DAY_OF_MONTH) + 1;
                        actualDays += repayDay1; // 加上还款日
                    }

                    // 首月应还利息 = 剩余本金 * 日利率 * 实际借款天数
                    BigDecimal firstMonthInterest = remainingPrincipal.multiply(dayRate)
                            .multiply(BigDecimal.valueOf(actualDays))
                            .setScale(2, RoundingMode.HALF_UP);
                    //累计利息
                    totalInterestAcc = totalInterestAcc.add(firstMonthInterest);
                    //剩余利息
                    remainingInterest = remainingInterest.subtract(firstMonthInterest);
                    // 首月应还本金 = 每月还款额 - 首月应还利息
                    BigDecimal firstMonthPrincipal = monthlyPayment.subtract(firstMonthInterest);

                    // 更新剩余本金
                    remainingPrincipal = remainingPrincipal.subtract(firstMonthPrincipal);

                    // 设置首月还款日期
                    Date firstRepaymentDate = new Date(calendar.getTimeInMillis());

                    RepaymentSchedule firstPayment = new RepaymentSchedule();
                    firstPayment.setOutId(outInfo.getOutId());
                    firstPayment.setAmount(monthlyPayment);
                    firstPayment.setPrincipal(firstMonthPrincipal);
                    firstPayment.setInterest(firstMonthInterest);
                    firstPayment.setInstallmentNumber(1);
                    firstPayment.setRepaymentDate(firstRepaymentDate);
                    firstPayment.setCreateTime(currentDate);
                    firstPayment.setPrincipalTodo(firstMonthPrincipal);
                    firstPayment.setInterestTodo(firstMonthInterest);
                    firstPayment.setIncomingId(incomingId);
                    schedule.add(firstPayment);

                    // 按月还款，增加一个月
                    calendar.add(Calendar.MONTH, 1);
                    calendar.set(Calendar.DAY_OF_MONTH, repayDay1);

                    // 次月及以后的计算
                    for (int period = 2; period <= loanTerm - 1; period++) {
                        // 每月应还利息 = 剩余本金 * 月利率
                        BigDecimal monthlyInterest = remainingPrincipal.multiply(monthlyRate).setScale(2, RoundingMode.HALF_UP);

                        // 每月应还本金 = 每月还款额 - 每月应还利息
                        BigDecimal monthlyPrincipal = monthlyPayment.subtract(monthlyInterest);
                        //如果剩余利息小于等于每月应还利息，则本月应还利息等于剩余利息
                        if (remainingInterest.compareTo(monthlyInterest) <= 0) {
                            monthlyInterest = remainingInterest;
                            monthlyPrincipal = monthlyPayment.subtract(monthlyInterest);
                        }
                        //累计利息
                        totalInterestAcc = totalInterestAcc.add(monthlyInterest);
                        // 更新剩余本金
                        remainingPrincipal = remainingPrincipal.subtract(monthlyPrincipal);

                        // 设置还款日期
                        Date repaymentDate = new Date(calendar.getTimeInMillis());

                        RepaymentSchedule payment = new RepaymentSchedule();
                        remainingInterest = remainingInterest.subtract(monthlyInterest);
                        //

                        payment.setOutId(outInfo.getOutId());
                        payment.setAmount(monthlyPayment);
                        payment.setPrincipal(monthlyPrincipal);
                        payment.setInterest(monthlyInterest);
                        payment.setInstallmentNumber(period);
                        payment.setRepaymentDate(repaymentDate);
                        payment.setCreateTime(currentDate);
                        payment.setPrincipalTodo(monthlyPrincipal);
                        payment.setInterestTodo(monthlyInterest);
                        payment.setIncomingId(incomingId);
                        schedule.add(payment);

                        // 按月还款，增加一个月
                        calendar.add(Calendar.MONTH, 1);
                        calendar.set(Calendar.DAY_OF_MONTH, repayDay1);
                    }

                    BigDecimal lastMonth = totalInterest.subtract(totalInterestAcc);
                    BigDecimal amount = lastMonth.add(remainingPrincipal);
                    Date repaymentDate = new Date(calendar.getTimeInMillis());
                    RepaymentSchedule payment = new RepaymentSchedule();
                    payment.setOutId(outInfo.getOutId());
                    payment.setAmount(amount);
                    payment.setPrincipal(remainingPrincipal);
                    payment.setInterest(lastMonth);
                    payment.setInstallmentNumber(loanTerm);
                    payment.setRepaymentDate(repaymentDate);
                    payment.setCreateTime(currentDate);
                    payment.setPrincipalTodo(remainingPrincipal);
                    payment.setInterestTodo(lastMonth);
                    payment.setIncomingId(incomingId);
                    schedule.add(payment);
                }
                break;
            }
            // 等本等息
            case SysConstant.REPAY_TYPE_TWO: {
                if (SysConstant.REPAY_FREQUENCY_WEEK.equals(repayFrequency)) {
                    // 每周应还本金 = 贷款本金 / 总还款周数
                    BigDecimal weeklyPrincipal = loanAmount.divide(BigDecimal.valueOf(loanTerm), 2, RoundingMode.HALF_UP);
                    int actualDays;
                    Calendar loanStartCalendar = Calendar.getInstance();
                    loanStartCalendar.setTime(new Date());
                    int currentDayOfWeek = loanStartCalendar.get(Calendar.DAY_OF_WEEK) - 1;
                    System.out.println(currentDayOfWeek);
                    //如果当前日期大于等于还款日期
                    if (currentDayOfWeek >= repayDay1) {
                        //放款日到周末的实际天数
                        actualDays = (Calendar.SATURDAY - currentDayOfWeek + 7) % 7 + 1;
                        //首周+实际放款日
                        actualDays = actualDays + repayDay1;
                    } else if (currentDayOfWeek == 0) {
                        actualDays = 7;
                    } else {
                        actualDays = repayDay1 - currentDayOfWeek + 1;
                    }

                    // 总利息 = 贷款本金 × 周利率 × (还款期数-（(7-首周)/7）)
                    totalInterest = loanAmount.multiply(weekRate)
                            .multiply(BigDecimal.valueOf(loanTerm).subtract(
                                    BigDecimal.valueOf(7).subtract(BigDecimal.valueOf(actualDays))
                                            .divide(BigDecimal.valueOf(7), 10, RoundingMode.HALF_UP)
                            ))
                            .setScale(2, RoundingMode.HALF_UP);
                    // 首周应还利息 = 贷款本金 × 周利率 × (实际借款天数 / 7)
                    BigDecimal firstWeekInterest = loanAmount.multiply(weekRate)
                            .multiply(BigDecimal.valueOf(actualDays))
                            .divide(BigDecimal.valueOf(7), 2, RoundingMode.HALF_UP);
                    totalInterestAcc = totalInterestAcc.add(firstWeekInterest);
                    remainingPrincipal = remainingPrincipal.subtract(weeklyPrincipal);

                    // 首月应还总额 = 每月应还本金 + 首月应还利息
                    BigDecimal firstWeekPayment = weeklyPrincipal.add(firstWeekInterest);
                    // 设置首月还款日期
                    Date firstRepaymentDate = new Date(calendar.getTimeInMillis());

                    RepaymentSchedule firstPayment = new RepaymentSchedule();
                    firstPayment.setOutId(outInfo.getOutId());
                    firstPayment.setAmount(firstWeekPayment);
                    firstPayment.setPrincipal(weeklyPrincipal);
                    firstPayment.setInterest(firstWeekInterest);
                    firstPayment.setInstallmentNumber(1);
                    firstPayment.setRepaymentDate(firstRepaymentDate);
                    firstPayment.setCreateTime(currentDate);
                    firstPayment.setPrincipalTodo(weeklyPrincipal);
                    firstPayment.setInterestTodo(firstWeekInterest);
                    firstPayment.setIncomingId(incomingId);
                    schedule.add(firstPayment);
                    // 按周还款，增加一周
                    calendar.add(Calendar.WEEK_OF_MONTH, 1);
                    // 次月及以后的计算
                    for (int period = 2; period <= loanTerm - 1; period++) {
                        // 每月应还利息 = 贷款本金 × 月利率
                        BigDecimal weeklyInterest = loanAmount.multiply(weekRate).setScale(2, RoundingMode.HALF_UP);

                        // 每周应还总额 = 每周应还本金 + 每周应还利息
                        BigDecimal monthlyPayment = weeklyPrincipal.add(weeklyInterest);
                        totalInterestAcc = totalInterestAcc.add(weeklyInterest);
                        remainingPrincipal = remainingPrincipal.subtract(weeklyPrincipal);
                        // 设置还款日期
                        Date repaymentDate = new Date(calendar.getTimeInMillis());

                        RepaymentSchedule payment = new RepaymentSchedule();
                        payment.setOutId(outInfo.getOutId());
                        payment.setAmount(monthlyPayment);
                        payment.setPrincipal(weeklyPrincipal);
                        payment.setInterest(weeklyInterest);
                        payment.setInstallmentNumber(period);
                        payment.setRepaymentDate(repaymentDate);
                        payment.setCreateTime(currentDate);
                        payment.setPrincipalTodo(weeklyPrincipal);
                        payment.setInterestTodo(weeklyInterest);
                        payment.setIncomingId(incomingId);
                        schedule.add(payment);
                        // 按周还款，增加一周
                        calendar.add(Calendar.WEEK_OF_MONTH, 1);
                    }
                    BigDecimal lastWeek = totalInterest.subtract(totalInterestAcc);
                    BigDecimal amount = lastWeek.add(remainingPrincipal);
                    Date repaymentDate = new Date(calendar.getTimeInMillis());
                    RepaymentSchedule payment = new RepaymentSchedule();
                    payment.setOutId(outInfo.getOutId());
                    payment.setAmount(amount);
                    payment.setPrincipal(remainingPrincipal);
                    payment.setInterest(lastWeek);
                    payment.setInstallmentNumber(loanTerm);
                    payment.setRepaymentDate(repaymentDate);
                    payment.setCreateTime(currentDate);
                    payment.setPrincipalTodo(remainingPrincipal);
                    payment.setInterestTodo(lastWeek);
                    payment.setIncomingId(incomingId);
                    schedule.add(payment);
                } else {
                    // 每月应还本金 = 贷款本金 / 总还款月数
                    BigDecimal monthlyPrincipal = loanAmount.divide(BigDecimal.valueOf(loanTerm), 2, RoundingMode.HALF_UP);
                    // 计算首月实际借款天数
                    Calendar loanStartCalendar = Calendar.getInstance();
                    loanStartCalendar.setTime(new Date());

                    int actualDays;

                    // 如果放款日是1号，则实际借款天数为30天
                    if (loanStartCalendar.get(Calendar.DAY_OF_MONTH) == 1) {
                        actualDays = 30; // 固定为30天
                    } else {
                        // 否则，计算从放款日到月底的实际天数
                        actualDays = loanStartCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) - loanStartCalendar.get(Calendar.DAY_OF_MONTH) + 1;
                        actualDays += repayDay1; // 加上还款日
                    }

                    // 总利息 = 贷款本金 × 月利率 × (还款期数-（(30-首月)/30）)
                    totalInterest = loanAmount.multiply(monthlyRate)
                            .multiply(BigDecimal.valueOf(loanTerm).subtract(
                                    BigDecimal.valueOf(30).subtract(BigDecimal.valueOf(actualDays))
                                            .divide(BigDecimal.valueOf(30), 10, RoundingMode.HALF_UP)
                            ))
                            .setScale(2, RoundingMode.HALF_UP);


                    // 首月应还利息 = 贷款本金 × 月利率 × (实际借款天数 / 30)
                    BigDecimal firstMonthInterest = loanAmount.multiply(monthlyRate)
                            .multiply(BigDecimal.valueOf(actualDays))
                            .divide(BigDecimal.valueOf(30), 2, RoundingMode.HALF_UP);
                    totalInterestAcc = totalInterestAcc.add(firstMonthInterest);
                    remainingPrincipal = remainingPrincipal.subtract(monthlyPrincipal);

                    // 首月应还总额 = 每月应还本金 + 首月应还利息
                    BigDecimal firstMonthPayment = monthlyPrincipal.add(firstMonthInterest);

                    // 设置首月还款日期
                    Date firstRepaymentDate = new Date(calendar.getTimeInMillis());

                    RepaymentSchedule firstPayment = new RepaymentSchedule();
                    firstPayment.setOutId(outInfo.getOutId());
                    firstPayment.setAmount(firstMonthPayment);
                    firstPayment.setPrincipal(monthlyPrincipal);
                    firstPayment.setInterest(firstMonthInterest);
                    firstPayment.setInstallmentNumber(1);
                    firstPayment.setRepaymentDate(firstRepaymentDate);
                    firstPayment.setCreateTime(currentDate);
                    firstPayment.setPrincipalTodo(monthlyPrincipal);
                    firstPayment.setInterestTodo(firstMonthInterest);
                    firstPayment.setIncomingId(incomingId);
                    schedule.add(firstPayment);

                    // 按月还款，增加一个月
                    calendar.add(Calendar.MONTH, 1);
                    calendar.set(Calendar.DAY_OF_MONTH, repayDay1);

                    // 次月及以后的计算
                    for (int period = 2; period <= loanTerm - 1; period++) {
                        // 每月应还利息 = 贷款本金 × 月利率
                        BigDecimal monthlyInterest = loanAmount.multiply(monthlyRate).setScale(2, RoundingMode.HALF_UP);

                        // 每月应还总额 = 每月应还本金 + 每月应还利息
                        BigDecimal monthlyPayment = monthlyPrincipal.add(monthlyInterest);
                        totalInterestAcc = totalInterestAcc.add(monthlyInterest);
                        remainingPrincipal = remainingPrincipal.subtract(monthlyPrincipal);
                        // 设置还款日期
                        Date repaymentDate = new Date(calendar.getTimeInMillis());

                        RepaymentSchedule payment = new RepaymentSchedule();
                        payment.setOutId(outInfo.getOutId());
                        payment.setAmount(monthlyPayment);
                        payment.setPrincipal(monthlyPrincipal);
                        payment.setInterest(monthlyInterest);
                        payment.setInstallmentNumber(period);
                        payment.setRepaymentDate(repaymentDate);
                        payment.setCreateTime(currentDate);
                        payment.setPrincipalTodo(monthlyPrincipal);
                        payment.setInterestTodo(monthlyInterest);
                        payment.setIncomingId(incomingId);
                        schedule.add(payment);

                        // 按月还款，增加一个月
                        calendar.add(Calendar.MONTH, 1);
                        calendar.set(Calendar.DAY_OF_MONTH, repayDay1);
                    }
                    BigDecimal lastMonth = totalInterest.subtract(totalInterestAcc);
                    BigDecimal amount = lastMonth.add(remainingPrincipal);
                    Date repaymentDate = new Date(calendar.getTimeInMillis());
                    RepaymentSchedule payment = new RepaymentSchedule();
                    payment.setOutId(outInfo.getOutId());
                    payment.setAmount(amount);
                    payment.setPrincipal(remainingPrincipal);
                    payment.setInterest(lastMonth);
                    payment.setInstallmentNumber(loanTerm);
                    payment.setRepaymentDate(repaymentDate);
                    payment.setCreateTime(currentDate);
                    payment.setPrincipalTodo(remainingPrincipal);
                    payment.setInterestTodo(lastMonth);
                    payment.setIncomingId(incomingId);
                    schedule.add(payment);

                }
                break;
            }
            // 一次性还本付息
            case SysConstant.REPAY_TYPE_THREE: {
                totalInterest = loanAmount.multiply(dayRate).multiply(term);
                BigDecimal periodToPay = totalInterest.add(loanAmount);

                // 设置还款日期
                calendar.add(Calendar.DAY_OF_MONTH, loanTerm);
                Date repaymentDate = new Date(calendar.getTimeInMillis());

                loanTerm = 1;
                RepaymentSchedule payment = new RepaymentSchedule();
                payment.setOutId(outInfo.getOutId());
                payment.setAmount(periodToPay);
                payment.setPrincipal(loanAmount);
                payment.setInterest(totalInterest);
                payment.setInstallmentNumber(1);
                payment.setRepaymentDate(repaymentDate);
                payment.setCreateTime(currentDate);
                payment.setPrincipalTodo(loanAmount);
                payment.setInterestTodo(totalInterest);
                payment.setIncomingId(incomingId);
                schedule.add(payment);
                break;
            }
            // 先息后本
            case SysConstant.REPAY_TYPE_FOUR: {
                if (SysConstant.REPAY_FREQUENCY_WEEK.equals(repayFrequency)) {
                    int actualDays;
                    Calendar loanStartCalendar = Calendar.getInstance();
                    loanStartCalendar.setTime(new Date());
                    int currentDayOfWeek = loanStartCalendar.get(Calendar.DAY_OF_WEEK) - 1;
                    System.out.println(currentDayOfWeek);
                    //如果当前日期大于等于还款日期
                    if (currentDayOfWeek >= repayDay1) {
                        //放款日到周末的实际天数
                        actualDays = (Calendar.SATURDAY - currentDayOfWeek + 7) % 7 + 1;
                        //首周+实际放款日
                        actualDays = actualDays + repayDay1;
                    } else if (currentDayOfWeek == 0) {
                        actualDays = 7;
                    } else {
                        actualDays = repayDay1 - currentDayOfWeek + 1;
                    }

                    // 首周应还利息 = 贷款本金 × 周利率 × (实际借款天数 / 7)
                    BigDecimal firstWeekInterest = loanAmount.multiply(weekRate)
                            .multiply(BigDecimal.valueOf(actualDays))
                            .divide(BigDecimal.valueOf(7), 2, RoundingMode.HALF_UP);
                    totalInterestAcc = totalInterestAcc.add(firstWeekInterest);

                    // 总利息 = 贷款本金 × 周利率 × (总还款周数 - (7 - 实际借款天数) / 7)
                    totalInterest = loanAmount.multiply(weekRate)
                            .multiply(BigDecimal.valueOf(loanTerm).subtract(
                                    BigDecimal.valueOf(7).subtract(BigDecimal.valueOf(actualDays))
                                            .divide(BigDecimal.valueOf(7), 10, RoundingMode.HALF_UP)
                            ))
                            .setScale(2, RoundingMode.HALF_UP);
                    // 首周应还总额 = 首周应还利息
                    BigDecimal firstWeekPayment = BigDecimal.ZERO;
                    BigDecimal loanAmount1 = BigDecimal.ZERO;
                    if (loanTerm == 1) {
                        firstWeekPayment = firstWeekPayment.add(loanAmount);
                        loanAmount1 = loanAmount1.add(loanAmount);
                    } else {
                        // 首月只还利息，本金为0
                        firstWeekPayment = firstWeekInterest;
                        loanAmount1 = BigDecimal.ZERO;
                    }
                    // 设置首月还款日期
                    Date firstRepaymentDate = new Date(calendar.getTimeInMillis());

                    RepaymentSchedule firstPayment = new RepaymentSchedule();
                    firstPayment.setOutId(outInfo.getOutId());
                    firstPayment.setAmount(firstWeekPayment);
                    firstPayment.setPrincipal(loanAmount1);
                    firstPayment.setInterest(firstWeekInterest);
                    firstPayment.setInstallmentNumber(1);
                    firstPayment.setRepaymentDate(firstRepaymentDate);
                    firstPayment.setCreateTime(currentDate);
                    firstPayment.setPrincipalTodo(BigDecimal.ZERO);
                    firstPayment.setInterestTodo(firstWeekInterest);
                    firstPayment.setIncomingId(incomingId);
                    schedule.add(firstPayment);

                    // 按月还款，增加一个月
                    calendar.add(Calendar.WEEK_OF_MONTH, 1);

                    // 中间周（第2周到第n-1周）的计算
                    for (int period = 2; period < loanTerm; period++) {
                        // 每周应还利息 = 贷款本金 × 周利率
                        BigDecimal weeklyInterest = loanAmount.multiply(weekRate).setScale(2, RoundingMode.HALF_UP);

                        // 每周应还总额 = 每周应还利息
                        BigDecimal weeklyPayment = weeklyInterest;
                        totalInterestAcc = totalInterestAcc.add(weeklyPayment);
                        // 设置还款日期
                        Date repaymentDate = new Date(calendar.getTimeInMillis());


                        RepaymentSchedule payment = new RepaymentSchedule();
                        payment.setOutId(outInfo.getOutId());
                        payment.setAmount(weeklyPayment);
                        payment.setPrincipal(BigDecimal.ZERO); // 中间月份只还利息，本金为0
                        payment.setInterest(weeklyInterest);
                        payment.setInstallmentNumber(period);
                        payment.setRepaymentDate(repaymentDate);
                        payment.setCreateTime(currentDate);
                        payment.setPrincipalTodo(BigDecimal.ZERO);
                        payment.setInterestTodo(weeklyInterest);
                        payment.setIncomingId(incomingId);
                        schedule.add(payment);
                        // 按周还款，增加一个周
                        calendar.add(Calendar.WEEK_OF_MONTH, 1);
                    }
                    // 最后1周的计算
                    if (loanTerm >= 2) {
                        // 最后1周应还利息 = 贷款本金 × 周利率
                        BigDecimal lastWeek = totalInterest.subtract(totalInterestAcc);
                        // 最后1周应还总额 = 贷款本金 + 最后1周应还利息
                        BigDecimal lastWeekPayment = loanAmount.add(lastWeek);

                        // 设置最后1周还款日期
                        Date lastRepaymentDate = new Date(calendar.getTimeInMillis());

                        RepaymentSchedule lastPayment = new RepaymentSchedule();
                        lastPayment.setOutId(outInfo.getOutId());
                        lastPayment.setAmount(lastWeekPayment);
                        lastPayment.setPrincipal(loanAmount); // 最后1月还本金
                        lastPayment.setInterest(lastWeek);
                        lastPayment.setInstallmentNumber(loanTerm);
                        lastPayment.setRepaymentDate(lastRepaymentDate);
                        lastPayment.setCreateTime(currentDate);
                        lastPayment.setPrincipalTodo(loanAmount);
                        lastPayment.setInterestTodo(lastWeek);
                        lastPayment.setIncomingId(incomingId);
                        schedule.add(lastPayment);
                    }

                } else {

                    // 计算首月实际借款天数
                    Calendar loanStartCalendar = Calendar.getInstance();
                    loanStartCalendar.setTime(new Date());

                    int actualDays;

                    // 如果放款日是1号，则实际借款天数为30天
                    if (loanStartCalendar.get(Calendar.DAY_OF_MONTH) == 1) {
                        actualDays = 30; // 固定为30天
                    } else {
                        // 否则，计算从放款日到月底的实际天数
                        actualDays = loanStartCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) - loanStartCalendar.get(Calendar.DAY_OF_MONTH) + 1;
                        actualDays += repayDay1; // 加上还款日
                    }

                    // 首月应还利息 = 贷款本金 × 月利率 × (实际借款天数 / 30)
                    BigDecimal firstMonthInterest = loanAmount.multiply(monthlyRate)
                            .multiply(BigDecimal.valueOf(actualDays))
                            .divide(BigDecimal.valueOf(30), 2, RoundingMode.HALF_UP);
                    totalInterestAcc = totalInterestAcc.add(firstMonthInterest);

                    // 总利息 = 贷款本金 × 月利率 × (总还款月数 - (30 - 实际借款天数) / 30)
                    totalInterest = loanAmount.multiply(monthlyRate)
                            .multiply(BigDecimal.valueOf(loanTerm).subtract(
                                    BigDecimal.valueOf(30).subtract(BigDecimal.valueOf(actualDays))
                                            .divide(BigDecimal.valueOf(30), 10, RoundingMode.HALF_UP)
                            ))
                            .setScale(2, RoundingMode.HALF_UP);

                    // 首月应还总额 = 首月应还利息
                    BigDecimal firstMonthPayment = BigDecimal.ZERO;
                    BigDecimal loanAmount1 = BigDecimal.ZERO;
                    if (loanTerm == 1) {
                        firstMonthPayment = firstMonthPayment.add(loanAmount);
                        loanAmount1 = loanAmount1.add(loanAmount);
                    } else {
                        // 首月只还利息，本金为0
                        firstMonthPayment = firstMonthInterest;
                        loanAmount1 = BigDecimal.ZERO;
                    }
                    // 设置首月还款日期
                    Date firstRepaymentDate = new Date(calendar.getTimeInMillis());

                    RepaymentSchedule firstPayment = new RepaymentSchedule();
                    firstPayment.setOutId(outInfo.getOutId());
                    firstPayment.setAmount(firstMonthPayment);
                    firstPayment.setPrincipal(loanAmount1);
                    firstPayment.setInterest(firstMonthInterest);
                    firstPayment.setInstallmentNumber(1);
                    firstPayment.setRepaymentDate(firstRepaymentDate);
                    firstPayment.setCreateTime(currentDate);
                    firstPayment.setPrincipalTodo(BigDecimal.ZERO);
                    firstPayment.setInterestTodo(firstMonthInterest);
                    firstPayment.setIncomingId(incomingId);
                    schedule.add(firstPayment);

                    // 按月还款，增加一个月
                    calendar.add(Calendar.MONTH, 1);
                    calendar.set(Calendar.DAY_OF_MONTH, repayDay1);

                    // 中间月份（第2月到第n-1月）的计算
                    for (int period = 2; period < loanTerm; period++) {
                        // 每月应还利息 = 贷款本金 × 月利率
                        BigDecimal monthlyInterest = loanAmount.multiply(monthlyRate).setScale(2, RoundingMode.HALF_UP);

                        // 每月应还总额 = 每月应还利息
                        BigDecimal monthlyPayment = monthlyInterest;
                        totalInterestAcc = totalInterestAcc.add(monthlyPayment);
                        // 设置还款日期
                        Date repaymentDate = new Date(calendar.getTimeInMillis());


                        RepaymentSchedule payment = new RepaymentSchedule();
                        payment.setOutId(outInfo.getOutId());
                        payment.setAmount(monthlyPayment);
                        payment.setPrincipal(BigDecimal.ZERO); // 中间月份只还利息，本金为0
                        payment.setInterest(monthlyInterest);
                        payment.setInstallmentNumber(period);
                        payment.setRepaymentDate(repaymentDate);
                        payment.setCreateTime(currentDate);
                        payment.setPrincipalTodo(BigDecimal.ZERO);
                        payment.setInterestTodo(monthlyInterest);
                        payment.setIncomingId(incomingId);
                        schedule.add(payment);

                        // 按月还款，增加一个月
                        calendar.add(Calendar.MONTH, 1);
                        calendar.set(Calendar.DAY_OF_MONTH, repayDay1);
                    }
                    // 最后1月的计算
                    if (loanTerm >= 2) {
                        // 最后1月应还利息 = 贷款本金 × 月利率
                        BigDecimal lastMonth = totalInterest.subtract(totalInterestAcc);
                        // 最后1月应还总额 = 贷款本金 + 最后1月应还利息
                        BigDecimal lastMonthPayment = loanAmount.add(lastMonth);

                        // 设置最后1月还款日期
                        Date lastRepaymentDate = new Date(calendar.getTimeInMillis());

                        RepaymentSchedule lastPayment = new RepaymentSchedule();
                        lastPayment.setOutId(outInfo.getOutId());
                        lastPayment.setAmount(lastMonthPayment);
                        lastPayment.setPrincipal(loanAmount); // 最后1月还本金
                        lastPayment.setInterest(lastMonth);
                        lastPayment.setInstallmentNumber(loanTerm);
                        lastPayment.setRepaymentDate(lastRepaymentDate);
                        lastPayment.setCreateTime(currentDate);
                        lastPayment.setPrincipalTodo(loanAmount);
                        lastPayment.setInterestTodo(lastMonth);
                        lastPayment.setIncomingId(incomingId);
                        schedule.add(lastPayment);
                    }
                }
                break;
            }
        }
        IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
        Company company = companyMapper.selectById(companyId);
        Debt debt = new Debt();
        if (outInfo.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
            Integer companyIdOrBlocId = companyId;
            if (!SysConstant.ACCOUNTING_TYPE_ONE.equals(company.getAccountingType())) {
                companyIdOrBlocId = company.getBlocId();
            }
            ProductCompany productCompany = productCompanyMapper.selectOne(new LambdaQueryWrapper<ProductCompany>()
                    .eq(ProductCompany::getCompanyId, companyIdOrBlocId)
                    .eq(ProductCompany::getProductId, incomingInfo.getProductId()));
            Integer expireTime = productCompany.getExpireTime();
            Calendar calendar1 = Calendar.getInstance();
            calendar1.add(Calendar.DAY_OF_MONTH, expireTime);
            Date date = new Date(calendar1.getTimeInMillis());
            debt.setDebtReleaseTime(date);
        }

        // 插入债券表
        debt.setIncomingId(incomingId);
        debt.setOutId(outInfo.getOutId());
        debt.setCompanyId(companyId);
        String isWithhold;
        if (SysConstant.SYSTEM_TYPE_WEB.equals(incomingInfo.getIncomingType())) {
            isWithhold = company.getWithholdSwitch();
        } else {
            Object value = redisTemplate.opsForValue().get(outInfo.getOutId() + "");
            isWithhold = value != null ? value.toString() : "1";
            redisTemplate.delete(outInfo.getOutId() + "");
        }
        debt.setIsWithhold(isWithhold);
        debt.setOutAmount(outInfo.getLoanAmount());
        debt.setRemainingRepaymentPeriods(loanTerm);
        debt.setDisbursementDate(new Date());
        debt.setShouldRepayPrincipal(loanAmount);
        debt.setShouldRepayInterest(totalInterest);
        debt.setRemainingShouldRepayPrincipal(loanAmount);
        debt.setRemainingShouldRepayInterest(totalInterest);
        debtMapper.insert(debt);
        log.info("新增债券表" + debt);

        // 插入还款计划表
        repaymentScheduleMapper.insertSchedule(schedule);
        log.info("新增还款计划表" + schedule);
        return schedule;
    }

    /**
     * 计算贷款还款计划
     *
     * @param outInfo 放款信息
     */


    @Override
    public void calculateInsuranceLoanSchedule(OutInfo outInfo, BigDecimal rate) {
        List<RepaymentSchedule> schedule = new ArrayList<>();
        String repayFrequency = outInfo.getRepayFrequency();
        BigDecimal loanAmount = outInfo.getLoanAmount();
        Integer companyId = outInfo.getCompanyId();
        Integer loanTerm = outInfo.getTerm();
        String repayDay = outInfo.getRepayDay();
        String repayType = outInfo.getLoanMethod();
        rate = rate.multiply(new BigDecimal(12)
                .divide(BigDecimal.valueOf(loanTerm), 10, RoundingMode.HALF_UP)
        );//后置服务费年利率
        Integer repayDay1 = 0;
        Integer incomingId = outInfo.getIncomingId();
        Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId));
        BigDecimal insuranceAmount = credit.getInsuranceAmount();//商业保险金额
//        // 计算利率
//        BigDecimal rate = "0".equals(repayFrequency) ?
//                rate.divide(BigDecimal.valueOf(36000), 10, RoundingMode.HALF_UP) : // 按天利率 = 年利率 / 360
//                rate.divide(BigDecimal.valueOf(1200), 10, RoundingMode.HALF_UP);   // 按月利率 = 年利率 / 12

        BigDecimal remainingPrincipal = loanAmount; // 剩余本金
        BigDecimal totalInterest = BigDecimal.ZERO;

        // 累计利息
        BigDecimal totalInterestAcc = BigDecimal.ZERO;

        BigDecimal term = new BigDecimal(loanTerm);

        // 获取当前日期
        Calendar calendar = Calendar.getInstance();
        Date currentDate = new Date(calendar.getTimeInMillis());


        // 计算首次还款日期
        if (SysConstant.REPAY_FREQUENCY_MONTH.equals(repayFrequency) || StringUtils.isBlank(repayFrequency)) {
            // 按月还款
            calendar.setTime(currentDate);
            repayDay1 = Integer.parseInt(repayDay);
            calendar.add(Calendar.MONTH, 1);
            calendar.set(Calendar.DAY_OF_MONTH, repayDay1);
        }

        switch (repayType) {
            // 等本等息
            case SysConstant.REPAY_TYPE_TWO: {
                // 月利率 = 年利率 / 12
                BigDecimal monthlyRate = rate.divide(BigDecimal.valueOf(1200), 10, RoundingMode.HALF_UP);

                // 每月应还本金 = 贷款本金 / 总还款月数
                BigDecimal monthlyPrincipal = loanAmount.divide(BigDecimal.valueOf(loanTerm), 2, RoundingMode.HALF_UP);

                // 计算首月实际借款天数
                Calendar loanStartCalendar = Calendar.getInstance();
                loanStartCalendar.setTime(new Date());

                int actualDays;

                // 如果放款日是1号，则实际借款天数为30天
                if (loanStartCalendar.get(Calendar.DAY_OF_MONTH) == 1) {
                    actualDays = 30; // 固定为30天
                } else {
                    // 否则，计算从放款日到月底的实际天数
                    actualDays = loanStartCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) - loanStartCalendar.get(Calendar.DAY_OF_MONTH) + 1;
                    actualDays += repayDay1; // 加上还款日
                }

                // 总利息 = 商业保险总金额 × 月利率 × (还款期数-（(30-首月)/30）)
                totalInterest = insuranceAmount.multiply(monthlyRate)
                        .multiply(BigDecimal.valueOf(loanTerm).subtract(
                                BigDecimal.valueOf(30).subtract(BigDecimal.valueOf(actualDays))
                                        .divide(BigDecimal.valueOf(30), 10, RoundingMode.HALF_UP)
                        ))
                        .setScale(2, RoundingMode.HALF_UP);

                // 首月应还利息 = 贷款本金 × 月利率 × (实际借款天数 / 30)
                BigDecimal firstMonthInterest = insuranceAmount.multiply(monthlyRate)
                        .multiply(BigDecimal.valueOf(actualDays))
                        .divide(BigDecimal.valueOf(30), 2, RoundingMode.HALF_UP);
                totalInterestAcc = totalInterestAcc.add(firstMonthInterest);
                remainingPrincipal = remainingPrincipal.subtract(monthlyPrincipal);

                // 首月应还总额 = 每月应还本金 + 首月应还利息
                BigDecimal firstMonthPayment = monthlyPrincipal.add(firstMonthInterest);

                // 设置首月还款日期
                Date firstRepaymentDate = new Date(calendar.getTimeInMillis());

                RepaymentSchedule firstPayment = new RepaymentSchedule();
                firstPayment.setOutId(outInfo.getOutId());
                firstPayment.setAmount(firstMonthPayment);
                firstPayment.setPrincipal(monthlyPrincipal);
                firstPayment.setInterest(firstMonthInterest);
                firstPayment.setInstallmentNumber(1);
                firstPayment.setRepaymentDate(firstRepaymentDate);
                firstPayment.setCreateTime(currentDate);
                firstPayment.setPrincipalTodo(monthlyPrincipal);
                firstPayment.setInterestTodo(firstMonthInterest);
                firstPayment.setIncomingId(incomingId);
                schedule.add(firstPayment);

                // 按月还款，增加一个月
                calendar.add(Calendar.MONTH, 1);
                calendar.set(Calendar.DAY_OF_MONTH, repayDay1);

                // 次月及以后的计算
                for (int period = 2; period <= loanTerm - 1; period++) {
                    // 每月应还利息 =商业保险总金额 × 月利率
                    BigDecimal monthlyInterest = insuranceAmount.multiply(monthlyRate).setScale(2, RoundingMode.HALF_UP);

                    // 每月应还总额 = 每月应还本金 + 每月应还利息
                    BigDecimal monthlyPayment = monthlyPrincipal.add(monthlyInterest);
                    totalInterestAcc = totalInterestAcc.add(monthlyInterest);
                    remainingPrincipal = remainingPrincipal.subtract(monthlyPrincipal);
                    // 设置还款日期
                    Date repaymentDate = new Date(calendar.getTimeInMillis());

                    RepaymentSchedule payment = new RepaymentSchedule();
                    payment.setOutId(outInfo.getOutId());
                    payment.setAmount(monthlyPayment);
                    payment.setPrincipal(monthlyPrincipal);
                    payment.setInterest(monthlyInterest);
                    payment.setInstallmentNumber(period);
                    payment.setRepaymentDate(repaymentDate);
                    payment.setCreateTime(currentDate);
                    payment.setPrincipalTodo(monthlyPrincipal);
                    payment.setInterestTodo(monthlyInterest);
                    payment.setIncomingId(incomingId);
                    schedule.add(payment);

                    // 按月还款，增加一个月
                    calendar.add(Calendar.MONTH, 1);
                    calendar.set(Calendar.DAY_OF_MONTH, repayDay1);
                }
                BigDecimal lastMonth = totalInterest.subtract(totalInterestAcc);
                BigDecimal amount = lastMonth.add(remainingPrincipal);
                Date repaymentDate = new Date(calendar.getTimeInMillis());
                RepaymentSchedule payment = new RepaymentSchedule();
                payment.setOutId(outInfo.getOutId());
                payment.setAmount(amount);
                payment.setPrincipal(remainingPrincipal);
                payment.setInterest(lastMonth);
                payment.setInstallmentNumber(loanTerm);
                payment.setRepaymentDate(repaymentDate);
                payment.setCreateTime(currentDate);
                payment.setPrincipalTodo(remainingPrincipal);
                payment.setInterestTodo(lastMonth);
                payment.setIncomingId(incomingId);
                schedule.add(payment);
                break;
            }
        }
        IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
        Debt debt = new Debt();
        Company company = companyMapper.selectById(companyId);
        if (outInfo.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
            Integer companyIdOrBlocId = companyId;
            if (!SysConstant.ACCOUNTING_TYPE_ONE.equals(company.getAccountingType())) {
                companyIdOrBlocId = company.getBlocId();
            }
            ProductCompany productCompany = productCompanyMapper.selectOne(new LambdaQueryWrapper<ProductCompany>()
                    .eq(ProductCompany::getCompanyId, companyIdOrBlocId)
                    .eq(ProductCompany::getProductId, incomingInfo.getProductId()));
            Integer expireTime = productCompany.getExpireTime();
            Calendar calendar1 = Calendar.getInstance();
            calendar1.add(Calendar.DAY_OF_MONTH, expireTime);
            Date date = new Date(calendar1.getTimeInMillis());
            debt.setDebtReleaseTime(date);
        }

        // 插入债券表

        debt.setIncomingId(incomingId);
        debt.setOutId(outInfo.getOutId());
/*        if (outInfo.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {
            debt.setIsReleased("1");
            debt.setDebtConversionStatus(3);
        }*/
        debt.setCompanyId(companyId);
        debt.setIsWithhold(company.getWithholdSwitch());
        debt.setOutAmount(outInfo.getLoanAmount());
        debt.setRemainingRepaymentPeriods(loanTerm);
        debt.setShouldRepayPrincipal(loanAmount);
        debt.setShouldRepayInterest(totalInterest);
        debt.setDisbursementDate(new Date());
        debt.setRemainingShouldRepayPrincipal(loanAmount);
        debt.setRemainingShouldRepayInterest(totalInterest);
        debtMapper.insert(debt);
        BigDecimal newRate = totalInterest.add(credit.getPreServiceFee()).multiply(BigDecimal.valueOf(12)).divide(loanAmount.multiply(term), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
        creditMapper.updatePostServiceFee(totalInterest, newRate, incomingId);
        outInfoMapper.updateRate(newRate, outInfo.getOutId());
        log.info("新增债券表" + debt);

        // 插入还款计划表
        repaymentScheduleMapper.insertSchedule(schedule);
        log.info("新增还款计划表" + schedule);
    }


}


